Tweak matcher (compute connected components in advance). Simple pattern matching with RAMification (incl. Python expressions) seems to work.
This commit is contained in:
parent
bed3529676
commit
4160a8953e
13 changed files with 388 additions and 70 deletions
|
|
@ -53,6 +53,8 @@ def bootstrap_scd(state: State) -> UUID:
|
||||||
_optional_edge = add_edge_element(f"{attribute_element_name}.optional_link", attribute_element, _optional_node)
|
_optional_edge = add_edge_element(f"{attribute_element_name}.optional_link", attribute_element, _optional_node)
|
||||||
return _name_model, _optional_model
|
return _name_model, _optional_model
|
||||||
|
|
||||||
|
##### SCD META-MODEL #####
|
||||||
|
|
||||||
# # CLASSES, i.e. elements typed by Class
|
# # CLASSES, i.e. elements typed by Class
|
||||||
# # Element
|
# # Element
|
||||||
element_node = add_node_element("Element")
|
element_node = add_node_element("Element")
|
||||||
|
|
@ -64,6 +66,7 @@ def bootstrap_scd(state: State) -> UUID:
|
||||||
model_ref_node = add_node_element("ModelRef")
|
model_ref_node = add_node_element("ModelRef")
|
||||||
# # Global Constraint
|
# # Global Constraint
|
||||||
glob_constr_node = add_node_element("GlobalConstraint")
|
glob_constr_node = add_node_element("GlobalConstraint")
|
||||||
|
|
||||||
# # ASSOCIATIONS, i.e. elements typed by Association
|
# # ASSOCIATIONS, i.e. elements typed by Association
|
||||||
# # Association
|
# # Association
|
||||||
assoc_edge = add_edge_element("Association", class_node, class_node)
|
assoc_edge = add_edge_element("Association", class_node, class_node)
|
||||||
|
|
@ -71,6 +74,7 @@ def bootstrap_scd(state: State) -> UUID:
|
||||||
inh_edge = add_edge_element("Inheritance", element_node, element_node)
|
inh_edge = add_edge_element("Inheritance", element_node, element_node)
|
||||||
# # Attribute Link
|
# # Attribute Link
|
||||||
attr_link_edge = add_edge_element("AttributeLink", element_node, attr_node)
|
attr_link_edge = add_edge_element("AttributeLink", element_node, attr_node)
|
||||||
|
|
||||||
# # INHERITANCES, i.e. elements typed by Inheritance
|
# # INHERITANCES, i.e. elements typed by Inheritance
|
||||||
# # Class inherits from Element
|
# # Class inherits from Element
|
||||||
add_edge_element("class_inh_element", class_node, element_node)
|
add_edge_element("class_inh_element", class_node, element_node)
|
||||||
|
|
@ -84,9 +88,11 @@ def bootstrap_scd(state: State) -> UUID:
|
||||||
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
|
# # ModelRef inherits from Attribute
|
||||||
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
|
# # ATTRIBUTES, i.e. elements typed by Attribute
|
||||||
# # Action Code # TODO: Update to ModelRef when action code is explicitly modelled
|
# # Action Code # TODO: Update to ModelRef when action code is explicitly modelled
|
||||||
action_code_node = add_node_element("ActionCode")
|
action_code_node = add_node_element("ActionCode")
|
||||||
|
|
||||||
# # MODELREFS, i.e. elements typed by ModelRef
|
# # MODELREFS, i.e. elements typed by ModelRef
|
||||||
# # Integer
|
# # Integer
|
||||||
integer_node = add_node_element("Integer", str(integer_type_root))
|
integer_node = add_node_element("Integer", str(integer_type_root))
|
||||||
|
|
@ -94,6 +100,7 @@ def bootstrap_scd(state: State) -> UUID:
|
||||||
string_node = add_node_element("String", str(string_type_root))
|
string_node = add_node_element("String", str(string_type_root))
|
||||||
# # Boolean
|
# # Boolean
|
||||||
boolean_node = add_node_element("Boolean", str(boolean_type_root))
|
boolean_node = add_node_element("Boolean", str(boolean_type_root))
|
||||||
|
|
||||||
# # ATTRIBUTE LINKS, i.e. elements typed by AttributeLink
|
# # ATTRIBUTE LINKS, i.e. elements typed by AttributeLink
|
||||||
# # name attribute of AttributeLink
|
# # name attribute of AttributeLink
|
||||||
attr_name_edge = add_edge_element("AttributeLink_name", attr_link_edge, string_node)
|
attr_name_edge = add_edge_element("AttributeLink_name", attr_link_edge, string_node)
|
||||||
|
|
@ -111,6 +118,7 @@ def bootstrap_scd(state: State) -> UUID:
|
||||||
assoc_s_u_c_edge = add_edge_element("Association_source_upper_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_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)
|
assoc_t_u_c_edge = add_edge_element("Association_target_upper_cardinality", assoc_edge, integer_node)
|
||||||
|
|
||||||
# # bootstrap primitive types
|
# # bootstrap primitive types
|
||||||
# # order is important, integer must be first
|
# # order is important, integer must be first
|
||||||
bootstrap_integer_type(mcl_root, integer_type_root, state)
|
bootstrap_integer_type(mcl_root, integer_type_root, state)
|
||||||
|
|
@ -118,6 +126,7 @@ def bootstrap_scd(state: State) -> UUID:
|
||||||
bootstrap_float_type(mcl_root, float_type_root, state)
|
bootstrap_float_type(mcl_root, float_type_root, state)
|
||||||
bootstrap_string_type(mcl_root, string_type_root, state)
|
bootstrap_string_type(mcl_root, string_type_root, state)
|
||||||
bootstrap_type_type(mcl_root, type_type_root, state)
|
bootstrap_type_type(mcl_root, type_type_root, state)
|
||||||
|
|
||||||
# # ATTRIBUTE ATTRIBUTES, assign 'name' and 'optional' attributes to all AttributeLinks
|
# # ATTRIBUTE ATTRIBUTES, assign 'name' and 'optional' attributes to all AttributeLinks
|
||||||
# # AttributeLink_name
|
# # AttributeLink_name
|
||||||
m_name, m_opt = add_attribute_attributes("AttributeLink_name", attr_name_edge)
|
m_name, m_opt = add_attribute_attributes("AttributeLink_name", attr_name_edge)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@ from services.scd import SCD
|
||||||
from framework.conformance import Conformance
|
from framework.conformance import Conformance
|
||||||
from services.od import OD
|
from services.od import OD
|
||||||
from transformation.ramify import ramify
|
from transformation.ramify import ramify
|
||||||
|
from services.bottom.V0 import Bottom
|
||||||
from services.primitives.integer_type import Integer
|
from services.primitives.integer_type import Integer
|
||||||
|
from pattern_matching import mvs_adapter
|
||||||
|
from pattern_matching.matcher import MatcherVF2
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
@ -62,6 +65,9 @@ def main():
|
||||||
int_type_id = state.read_dict(state.read_root(), "Integer")
|
int_type_id = state.read_dict(state.read_root(), "Integer")
|
||||||
int_type = UUID(state.read_value(int_type_id))
|
int_type = UUID(state.read_value(int_type_id))
|
||||||
|
|
||||||
|
string_type_id = state.read_dict(state.read_root(), "String")
|
||||||
|
string_type = UUID(state.read_value(string_type_id))
|
||||||
|
|
||||||
# scd2 = SCD(scd_node, state)
|
# scd2 = SCD(scd_node, state)
|
||||||
# for el in scd2.list_elements():
|
# for el in scd2.list_elements():
|
||||||
# print(el)
|
# print(el)
|
||||||
|
|
@ -70,7 +76,7 @@ def main():
|
||||||
model_id = state.create_node()
|
model_id = state.create_node()
|
||||||
scd = SCD(model_id, state)
|
scd = SCD(model_id, state)
|
||||||
scd.create_class("Abstract", abstract=True)
|
scd.create_class("Abstract", abstract=True)
|
||||||
scd.create_class("A", min_c=1, max_c=10)
|
scd.create_class("A", min_c=1, max_c=2)
|
||||||
scd.create_inheritance("A", "Abstract")
|
scd.create_inheritance("A", "Abstract")
|
||||||
scd.create_model_ref("Integer", int_type)
|
scd.create_model_ref("Integer", int_type)
|
||||||
scd.create_attribute_link("A", "Integer", "size", False)
|
scd.create_attribute_link("A", "Integer", "size", False)
|
||||||
|
|
@ -97,16 +103,46 @@ def main():
|
||||||
od = OD(model_id, inst_id, state)
|
od = OD(model_id, inst_id, state)
|
||||||
|
|
||||||
od.create_object("a", "A")
|
od.create_object("a", "A")
|
||||||
|
od.create_object("a2", "A")
|
||||||
od.create_object("b", "B")
|
od.create_object("b", "B")
|
||||||
od.create_link("A2B", "a", "b")
|
od.create_link("A2B", "a", "b")
|
||||||
|
od.create_link("A2B", "a2", "b")
|
||||||
|
|
||||||
od.create_slot("size", "a", od.create_integer_value(42))
|
od.create_slot("size", "a", od.create_integer_value("a.size", 42))
|
||||||
|
|
||||||
print("checking conformance....")
|
print("checking conformance....")
|
||||||
conf2 = Conformance(state, inst_id, model_id)
|
conf2 = Conformance(state, inst_id, model_id)
|
||||||
print("conforms?", conf2.check_nominal(log=True))
|
print("conforms?", conf2.check_nominal(log=True))
|
||||||
|
|
||||||
ramify(state, model_id)
|
ramified_MM_id = ramify(state, model_id)
|
||||||
|
|
||||||
|
pattern_id = state.create_node()
|
||||||
|
pattern = OD(ramified_MM_id, pattern_id, state)
|
||||||
|
|
||||||
|
pattern.create_object("a1", "A")
|
||||||
|
pattern.create_slot("size", "a1", pattern.create_string_value("a1.size", 'v < 100'))
|
||||||
|
# pattern.create_object("a2", "A")
|
||||||
|
# pattern.create_slot("size", "a2", pattern.create_string_value("a2.size", '99'))
|
||||||
|
|
||||||
|
pattern.create_object("b1", "B")
|
||||||
|
# pattern.create_link("A2B", "a1", "b1")
|
||||||
|
|
||||||
|
conf3 = Conformance(state, pattern_id, ramified_MM_id)
|
||||||
|
print("conforms?", conf3.check_nominal(log=True))
|
||||||
|
|
||||||
|
host = mvs_adapter.model_to_graph(state, inst_id)
|
||||||
|
guest = mvs_adapter.model_to_graph(state, pattern_id)
|
||||||
|
|
||||||
|
print(host.vtxs)
|
||||||
|
print(host.edges)
|
||||||
|
|
||||||
|
print("matching...")
|
||||||
|
matcher = MatcherVF2(host, guest, mvs_adapter.RAMCompare(Bottom(state)))
|
||||||
|
prev = None
|
||||||
|
for m in matcher.match():
|
||||||
|
print("\nMATCH:\n", m)
|
||||||
|
input()
|
||||||
|
print("DONE")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -200,3 +200,33 @@ class GraphGenerator(object):
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
return pattern
|
return pattern
|
||||||
|
|
||||||
|
def get_random_host_and_guest(nr_vtxs, nr_vtx_types, nr_edges, nr_edge_types, pattern_nr_vtxs=3, pattern_nr_edges=15):
|
||||||
|
dv = [random.randint(0, nr_vtx_types) for _ in range(nr_vtxs)]
|
||||||
|
de = [random.randint(0, nr_edge_types) for _ in range(nr_edges)]
|
||||||
|
dc_inc = [random.randint(0, nr_vtxs-1) for _ in range(nr_edges)]
|
||||||
|
dc_out = [random.randint(0, nr_vtxs-1) for _ in range(nr_edges)]
|
||||||
|
|
||||||
|
return get_host_and_guest(dv, de, dc_inc, dc_out, pattern_nr_vtxs, pattern_nr_edges)
|
||||||
|
|
||||||
|
def get_host_and_guest(dv, de, dc_inc, dc_out, pattern_nr_vtxs=3, pattern_nr_edges=15):
|
||||||
|
gg = GraphGenerator(dv, de, dc_inc, dc_out)
|
||||||
|
graph = gg.getRandomGraph()
|
||||||
|
pattern = gg.getRandomPattern(pattern_nr_vtxs, pattern_nr_edges, debug=False)
|
||||||
|
return (graph, pattern)
|
||||||
|
|
||||||
|
|
||||||
|
def get_large_host_and_guest():
|
||||||
|
dv = [ 10,5,4,0,8,6,8,0,4,8,5,5,7,0,10,0,5,6,10,4,0,3,0,8,2,7,5,8,1,0,2,10,0,0,1,6,8,4,7,6,4,2,10,10,6,4,6,0,2,7 ]
|
||||||
|
de = [ 8,10,8,1,6,7,4,3,5,2,0,0,9,6,0,3,8,3,2,7,2,3,10,8,10,8,10,2,5,5,10,6,7,5,1,2,1,2,2,3,7,7,2,1,7,2,9,10,8,1,9,4,1,3,1,1,8,2,2,9,10,9,1,9,4,10,10,10,9,3,5,3,6,6,9,1,2,6,3,2,4,10,9,6,5,6,2,4,3,2,4,10,6,2,8,8,0,5,1,7,3,4,3,8,7,3,0,8,3,3,8,5,10,5,9,3,1,10,3,2,6,3,10,0,5,10,9,10,0,1,4,7,10,3,1,9,1,2,3,7,4,3,7,8,8,4,5,10,1,4 ]
|
||||||
|
dc_inc = [ 0,25,18,47,22,25,16,45,38,25,5,45,15,44,17,46,6,17,35,8,16,29,48,47,25,34,4,20,24,1,47,44,8,25,32,3,16,6,33,21,6,13,41,10,17,25,21,33,31,30,5,4,45,26,16,42,12,25,29,3,32,30,14,26,11,13,7,13,3,43,43,22,48,37,20,28,15,40,19,33,43,16,49,36,11,25,9,42,3,22,16,40,42,44,27,30,1,18,10,35,19,6,9,43,37,38,45,19,41,14,37,45,0,31,29,31,24,20,44,46,8,45,43,3,38,38,35,12,19,45,7,34,20,28,12,17,45,17,35,49,20,21,49,1,35,38,38,36,33,30 ]
|
||||||
|
dc_out = [ 9,2,49,49,37,33,16,21,5,46,4,15,9,6,14,22,16,33,23,21,15,31,37,23,47,3,30,26,35,9,29,21,39,32,22,43,5,9,41,30,31,30,37,33,31,34,23,22,34,26,44,36,38,33,48,5,9,34,13,7,48,41,43,26,26,7,12,6,12,28,22,8,29,22,24,27,16,4,31,41,32,15,19,20,38,0,26,18,43,46,40,17,29,14,34,14,32,17,32,47,16,45,7,4,35,22,42,11,38,2,0,29,4,38,17,44,9,23,5,10,31,17,1,11,16,5,37,27,35,32,45,16,18,1,14,4,42,24,43,31,21,38,6,34,39,46,20,1,38,47 ]
|
||||||
|
return get_host_and_guest(dv, de, dc_inc, dc_out)
|
||||||
|
|
||||||
|
def get_small_host_and_guest():
|
||||||
|
dv = [0, 1, 0, 1, 0]
|
||||||
|
de = [0, 0, 0]
|
||||||
|
dc_inc = [0, 2, 4]
|
||||||
|
dc_out = [1, 3, 3]
|
||||||
|
return get_host_and_guest(dv, de, dc_inc, dc_out)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ class Graph(object):
|
||||||
# redundant type keeping, "needed" for fast iterating over specific type
|
# redundant type keeping, "needed" for fast iterating over specific type
|
||||||
self.vertices = {} # {type, set(v1, v2, ...)}
|
self.vertices = {} # {type, set(v1, v2, ...)}
|
||||||
self.edges = {} # {type, set(e1, e2, ...)}
|
self.edges = {} # {type, set(e1, e2, ...)}
|
||||||
|
self.num_vertices = 0
|
||||||
|
|
||||||
def addCreateVertex(self, str_type):
|
def addCreateVertex(self, str_type):
|
||||||
"""
|
"""
|
||||||
|
|
@ -114,6 +115,8 @@ class Graph(object):
|
||||||
raise TypeError('addVertex expects a Vertex')
|
raise TypeError('addVertex expects a Vertex')
|
||||||
# add vertex, but it first creates a new set for the vertex type
|
# add vertex, but it first creates a new set for the vertex type
|
||||||
# if the type does not exist in the dictionary
|
# if the type does not exist in the dictionary
|
||||||
|
if vertex not in self.vertices.get(vertex.type, set()):
|
||||||
|
self.num_vertices += 1
|
||||||
self.vertices.setdefault(vertex.type, set()).add(vertex)
|
self.vertices.setdefault(vertex.type, set()).add(vertex)
|
||||||
|
|
||||||
def getVerticesOfType(self, str_type):
|
def getVerticesOfType(self, str_type):
|
||||||
|
|
|
||||||
|
|
@ -31,34 +31,17 @@ if __name__ == '__main__':
|
||||||
"""
|
"""
|
||||||
The main function called when running from the command line.
|
The main function called when running from the command line.
|
||||||
"""
|
"""
|
||||||
nr_of_vertices = 50
|
random.seed(0)
|
||||||
nr_of_diff_types_v = 2
|
|
||||||
nr_of_edges = 150
|
|
||||||
nr_of_diff_types_e = 2
|
|
||||||
|
|
||||||
dv = [random.randint(0, nr_of_diff_types_v) for _ in range(nr_of_vertices)]
|
graph, pattern = get_random_host_and_guest(
|
||||||
de = [random.randint(0, nr_of_diff_types_e) for _ in range(nr_of_edges)]
|
nr_vtxs = 10,
|
||||||
dc_inc = [random.randint(0, nr_of_vertices-1) for _ in range(nr_of_edges)]
|
nr_vtx_types = 0,
|
||||||
dc_out = [random.randint(0, nr_of_vertices-1) for _ in range(nr_of_edges)]
|
nr_edges = 20,
|
||||||
|
nr_edge_types = 0,
|
||||||
|
)
|
||||||
|
|
||||||
# override random graph by copy pasting output from terminal
|
# graph, pattern = get_large_host_and_guest()
|
||||||
# dv = [ 10,5,4,0,8,6,8,0,4,8,5,5,7,0,10,0,5,6,10,4,0,3,0,8,2,7,5,8,1,0,2,10,0,0,1,6,8,4,7,6,4,2,10,10,6,4,6,0,2,7 ]
|
# graph, pattern = get_small_host_and_guest()
|
||||||
# de = [ 8,10,8,1,6,7,4,3,5,2,0,0,9,6,0,3,8,3,2,7,2,3,10,8,10,8,10,2,5,5,10,6,7,5,1,2,1,2,2,3,7,7,2,1,7,2,9,10,8,1,9,4,1,3,1,1,8,2,2,9,10,9,1,9,4,10,10,10,9,3,5,3,6,6,9,1,2,6,3,2,4,10,9,6,5,6,2,4,3,2,4,10,6,2,8,8,0,5,1,7,3,4,3,8,7,3,0,8,3,3,8,5,10,5,9,3,1,10,3,2,6,3,10,0,5,10,9,10,0,1,4,7,10,3,1,9,1,2,3,7,4,3,7,8,8,4,5,10,1,4 ]
|
|
||||||
# dc_inc = [ 0,25,18,47,22,25,16,45,38,25,5,45,15,44,17,46,6,17,35,8,16,29,48,47,25,34,4,20,24,1,47,44,8,25,32,3,16,6,33,21,6,13,41,10,17,25,21,33,31,30,5,4,45,26,16,42,12,25,29,3,32,30,14,26,11,13,7,13,3,43,43,22,48,37,20,28,15,40,19,33,43,16,49,36,11,25,9,42,3,22,16,40,42,44,27,30,1,18,10,35,19,6,9,43,37,38,45,19,41,14,37,45,0,31,29,31,24,20,44,46,8,45,43,3,38,38,35,12,19,45,7,34,20,28,12,17,45,17,35,49,20,21,49,1,35,38,38,36,33,30 ]
|
|
||||||
# dc_out = [ 9,2,49,49,37,33,16,21,5,46,4,15,9,6,14,22,16,33,23,21,15,31,37,23,47,3,30,26,35,9,29,21,39,32,22,43,5,9,41,30,31,30,37,33,31,34,23,22,34,26,44,36,38,33,48,5,9,34,13,7,48,41,43,26,26,7,12,6,12,28,22,8,29,22,24,27,16,4,31,41,32,15,19,20,38,0,26,18,43,46,40,17,29,14,34,14,32,17,32,47,16,45,7,4,35,22,42,11,38,2,0,29,4,38,17,44,9,23,5,10,31,17,1,11,16,5,37,27,35,32,45,16,18,1,14,4,42,24,43,31,21,38,6,34,39,46,20,1,38,47 ]
|
|
||||||
|
|
||||||
dv = [0, 1, 0, 1, 0]
|
|
||||||
de = [0, 0, 0]
|
|
||||||
dc_inc = [0, 2, 4]
|
|
||||||
dc_out = [1, 3, 3]
|
|
||||||
|
|
||||||
gg = GraphGenerator(dv, de, dc_inc, dc_out, debug)
|
|
||||||
|
|
||||||
graph = gg.getRandomGraph()
|
|
||||||
|
|
||||||
print(graph.vertices)
|
|
||||||
pattern = gg.getRandomPattern(3, 15, debug=debug)
|
|
||||||
print(pattern.vertices)
|
|
||||||
|
|
||||||
# override random pattern by copy pasting output from terminal to create
|
# override random pattern by copy pasting output from terminal to create
|
||||||
# pattern, paste it in the createConstantPattern function in the generator.py
|
# pattern, paste it in the createConstantPattern function in the generator.py
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,34 @@
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
|
from util.timer import Timer
|
||||||
|
|
||||||
|
# like finding the 'strongly connected componenets', but edges are navigable in any direction
|
||||||
|
def find_connected_components(graph):
|
||||||
|
next_component = 0
|
||||||
|
vtx_to_component = {}
|
||||||
|
component_to_vtxs = []
|
||||||
|
for vtx in graph.vtxs:
|
||||||
|
if vtx in vtx_to_component:
|
||||||
|
continue
|
||||||
|
vtx_to_component[vtx] = next_component
|
||||||
|
vtxs = []
|
||||||
|
component_to_vtxs.append(vtxs)
|
||||||
|
add_recursively(vtx, vtxs, vtx_to_component, next_component)
|
||||||
|
next_component += 1
|
||||||
|
return (vtx_to_component, component_to_vtxs)
|
||||||
|
|
||||||
|
def add_recursively(vtx, vtxs: list, d: dict, component: int, already_visited: set = set()):
|
||||||
|
if vtx in already_visited:
|
||||||
|
return
|
||||||
|
already_visited.add(vtx)
|
||||||
|
vtxs.append(vtx)
|
||||||
|
d[vtx] = component
|
||||||
|
for edge in vtx.outgoing:
|
||||||
|
add_recursively(edge.tgt, vtxs, d, component, already_visited)
|
||||||
|
for edge in vtx.incoming:
|
||||||
|
add_recursively(edge.src, vtxs, d, component, already_visited)
|
||||||
|
|
||||||
class Graph:
|
class Graph:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.vtxs = []
|
self.vtxs = []
|
||||||
|
|
@ -18,13 +46,19 @@ class Vertex:
|
||||||
return f"V({self.value})"
|
return f"V({self.value})"
|
||||||
|
|
||||||
class Edge:
|
class Edge:
|
||||||
def __init__(self, src: Vertex, tgt: Vertex):
|
def __init__(self, src: Vertex, tgt: Vertex, label=None):
|
||||||
self.src = src
|
self.src = src
|
||||||
self.tgt = tgt
|
self.tgt = tgt
|
||||||
|
self.label = label
|
||||||
|
|
||||||
|
# Add ourselves to src/tgt vertices
|
||||||
self.src.outgoing.append(self)
|
self.src.outgoing.append(self)
|
||||||
self.tgt.incoming.append(self)
|
self.tgt.incoming.append(self)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
if self.label != None:
|
||||||
|
return f"E({self.src}--{self.label}->{self.tgt})"
|
||||||
|
else:
|
||||||
return f"E({self.src}->{self.tgt})"
|
return f"E({self.src}->{self.tgt})"
|
||||||
|
|
||||||
class MatcherState:
|
class MatcherState:
|
||||||
|
|
@ -38,8 +72,7 @@ class MatcherState:
|
||||||
self.h_unmatched_vtxs = []
|
self.h_unmatched_vtxs = []
|
||||||
self.g_unmatched_vtxs = []
|
self.g_unmatched_vtxs = []
|
||||||
|
|
||||||
# the most recently added pair of (guest,host) vertices
|
# boundary is the most recently added (to the mapping) pair of (guest -> host) vertices
|
||||||
# will always try to grow mapping via outgoing/incoming edges of this pair before attempting other non-connected vertices
|
|
||||||
self.boundary = None
|
self.boundary = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -89,6 +122,9 @@ class MatcherState:
|
||||||
((ge,he) for ge,he in self.mapping_edges.items()),
|
((ge,he) for ge,he in self.mapping_edges.items()),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
# return self.make_hashable().__repr__()
|
||||||
|
return "VTXS: "+self.mapping_vtxs.__repr__()+"\nEDGES: "+self.mapping_edges.__repr__()
|
||||||
|
|
||||||
class MatcherVF2:
|
class MatcherVF2:
|
||||||
# Guest is the pattern
|
# Guest is the pattern
|
||||||
|
|
@ -97,6 +133,14 @@ class MatcherVF2:
|
||||||
self.guest = guest
|
self.guest = guest
|
||||||
self.compare_fn = compare_fn
|
self.compare_fn = compare_fn
|
||||||
|
|
||||||
|
with Timer("find_connected_components - host"):
|
||||||
|
self.host_vtx_to_component, self.host_component_to_vtxs = find_connected_components(host)
|
||||||
|
with Timer("find_connected_components - guest"):
|
||||||
|
self.guest_vtx_to_component, self.guest_component_to_vtxs = find_connected_components(guest)
|
||||||
|
|
||||||
|
print("number of host connected components:", len(self.host_component_to_vtxs))
|
||||||
|
print("number of guest connected components:", len(self.guest_component_to_vtxs))
|
||||||
|
|
||||||
def match(self):
|
def match(self):
|
||||||
yield from self._match(
|
yield from self._match(
|
||||||
state=MatcherState.make_initial(self.host, self.guest),
|
state=MatcherState.make_initial(self.host, self.guest),
|
||||||
|
|
@ -104,26 +148,30 @@ class MatcherVF2:
|
||||||
|
|
||||||
|
|
||||||
def _match(self, state, already_visited, indent=0):
|
def _match(self, state, already_visited, indent=0):
|
||||||
|
# input()
|
||||||
|
|
||||||
def print_debug(*args):
|
def print_debug(*args):
|
||||||
pass
|
pass
|
||||||
# print(*args) # uncomment to see a trace of the matching process
|
# print(" "*indent, *args) # uncomment to see a trace of the matching process
|
||||||
|
|
||||||
print_debug(" "*indent, "match")
|
print_debug("match")
|
||||||
|
|
||||||
hashable_state = state.make_hashable()
|
# Keep track of the states in the search space that we already visited
|
||||||
if hashable_state in already_visited:
|
hashable = state.make_hashable()
|
||||||
print_debug(" "*indent, " SKIP - ALREADY VISITED")
|
if hashable in already_visited:
|
||||||
print_debug(" "*indent, " ", hashable_state)
|
print_debug(" SKIP - ALREADY VISITED")
|
||||||
|
# print_debug(" ", hashable)
|
||||||
return
|
return
|
||||||
print_debug(" "*indent, " ADD STATE")
|
# print_debug(" ", [hash(a) for a in already_visited])
|
||||||
print_debug(" "*indent, " ", hashable_state)
|
# print_debug(" ADD STATE")
|
||||||
already_visited.add(hashable_state)
|
# print_debug(" ", hash(hashable))
|
||||||
|
already_visited.add(hashable)
|
||||||
|
|
||||||
|
|
||||||
if len(state.mapping_vtxs) == len(self.guest.vtxs) and len(state.mapping_edges) == len(self.guest.edges):
|
if len(state.mapping_vtxs) == len(self.guest.vtxs) and len(state.mapping_edges) == len(self.guest.edges):
|
||||||
print_debug(" "*indent, "GOT MATCH:")
|
print_debug("GOT MATCH:")
|
||||||
print_debug(" "*indent, " ", state.mapping_vtxs)
|
print_debug(" ", state.mapping_vtxs)
|
||||||
print_debug(" "*indent, " ", state.mapping_edges)
|
print_debug(" ", state.mapping_edges)
|
||||||
yield state
|
yield state
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -136,61 +184,82 @@ class MatcherVF2:
|
||||||
raise Exception("wtf!")
|
raise Exception("wtf!")
|
||||||
|
|
||||||
def attempt_grow(direction, indent):
|
def attempt_grow(direction, indent):
|
||||||
print_debug(" "*indent, 'attempt_grow', direction)
|
for g_matched_vtx, h_matched_vtx in state.mapping_vtxs.items():
|
||||||
if state.boundary != None:
|
print_debug('attempt_grow', direction)
|
||||||
g_vtx, h_vtx = state.boundary
|
for g_candidate_edge in getattr(g_matched_vtx, direction):
|
||||||
for g_candidate_edge in getattr(g_vtx, direction):
|
print_debug('g_candidate_edge:', g_candidate_edge)
|
||||||
print_debug(" "*indent, 'g_candidate_edge:', g_candidate_edge)
|
|
||||||
if g_candidate_edge in state.mapping_edges:
|
|
||||||
print_debug(" "*indent, " skip, guest edge already matched")
|
|
||||||
continue # skip already matched guest edge
|
|
||||||
g_candidate_vtx = read_edge(g_candidate_edge, direction)
|
g_candidate_vtx = read_edge(g_candidate_edge, direction)
|
||||||
for h_candidate_edge in getattr(h_vtx, direction):
|
# g_to_skip_vtxs.add(g_candidate_vtx)
|
||||||
print_debug(" "*indent, 'h_candidate_edge:', h_candidate_edge)
|
if g_candidate_edge in state.mapping_edges:
|
||||||
|
print_debug(" skip, guest edge already matched")
|
||||||
|
continue # skip already matched guest edge
|
||||||
|
for h_candidate_edge in getattr(h_matched_vtx, direction):
|
||||||
|
if g_candidate_edge.label != h_candidate_edge.label:
|
||||||
|
print_debug(" labels differ")
|
||||||
|
continue
|
||||||
|
print_debug('h_candidate_edge:', h_candidate_edge)
|
||||||
if h_candidate_edge in state.r_mapping_edges:
|
if h_candidate_edge in state.r_mapping_edges:
|
||||||
print_debug(" "*indent, " skip, host edge already matched")
|
print_debug(" skip, host edge already matched")
|
||||||
continue # skip already matched host edge
|
continue # skip already matched host edge
|
||||||
h_candidate_vtx = read_edge(h_candidate_edge, direction)
|
h_candidate_vtx = read_edge(h_candidate_edge, direction)
|
||||||
print_debug(" "*indent, 'grow edge', g_candidate_edge, ':', h_candidate_edge)
|
print_debug('grow edge', g_candidate_edge, ':', h_candidate_edge, id(g_candidate_edge), id(h_candidate_edge))
|
||||||
new_state = state.grow_edge(h_candidate_edge, g_candidate_edge)
|
new_state = state.grow_edge(h_candidate_edge, g_candidate_edge)
|
||||||
yield from attempt_match_vtxs(
|
yield from attempt_match_vtxs(
|
||||||
new_state,
|
new_state,
|
||||||
g_candidate_vtx,
|
g_candidate_vtx,
|
||||||
h_candidate_vtx,
|
h_candidate_vtx,
|
||||||
indent+1)
|
indent+1)
|
||||||
|
print_debug('backtrack edge', g_candidate_edge, ':', h_candidate_edge, id(g_candidate_edge), id(h_candidate_edge))
|
||||||
|
|
||||||
def attempt_match_vtxs(state, g_candidate_vtx, h_candidate_vtx, indent):
|
def attempt_match_vtxs(state, g_candidate_vtx, h_candidate_vtx, indent):
|
||||||
print_debug(" "*indent, 'attempt_match_vtxs')
|
print_debug('attempt_match_vtxs')
|
||||||
if g_candidate_vtx in state.mapping_vtxs:
|
if g_candidate_vtx in state.mapping_vtxs:
|
||||||
if state.mapping_vtxs[g_candidate_vtx] != h_candidate_vtx:
|
if state.mapping_vtxs[g_candidate_vtx] != h_candidate_vtx:
|
||||||
print_debug(" "*indent, " nope, guest already mapped (mismatch)")
|
print_debug(" nope, guest already mapped (mismatch)")
|
||||||
return # guest vtx is already mapped but doesn't match host vtx
|
return # guest vtx is already mapped but doesn't match host vtx
|
||||||
if h_candidate_vtx in state.r_mapping_vtxs:
|
if h_candidate_vtx in state.r_mapping_vtxs:
|
||||||
if state.r_mapping_vtxs[h_candidate_vtx] != g_candidate_vtx:
|
if state.r_mapping_vtxs[h_candidate_vtx] != g_candidate_vtx:
|
||||||
print_debug(" "*indent, " nope, host already mapped (mismatch)")
|
print_debug(" nope, host already mapped (mismatch)")
|
||||||
return # host vtx is already mapped but doesn't match guest vtx
|
return # host vtx is already mapped but doesn't match guest vtx
|
||||||
g_outdegree = len(g_candidate_vtx.outgoing)
|
g_outdegree = len(g_candidate_vtx.outgoing)
|
||||||
h_outdegree = len(h_candidate_vtx.outgoing)
|
h_outdegree = len(h_candidate_vtx.outgoing)
|
||||||
if g_outdegree > h_outdegree:
|
if g_outdegree > h_outdegree:
|
||||||
|
print_debug(" nope, outdegree")
|
||||||
return
|
return
|
||||||
g_indegree = len(g_candidate_vtx.incoming)
|
g_indegree = len(g_candidate_vtx.incoming)
|
||||||
h_indegree = len(h_candidate_vtx.incoming)
|
h_indegree = len(h_candidate_vtx.incoming)
|
||||||
if g_indegree > h_indegree:
|
if g_indegree > h_indegree:
|
||||||
|
print_debug(" nope, indegree")
|
||||||
return
|
return
|
||||||
if not self.compare_fn(g_candidate_vtx.value, h_candidate_vtx.value):
|
if not self.compare_fn(g_candidate_vtx.value, h_candidate_vtx.value):
|
||||||
|
print_debug(" nope, bad compare")
|
||||||
return
|
return
|
||||||
new_state = state.grow_vtx(
|
new_state = state.grow_vtx(
|
||||||
h_candidate_vtx,
|
h_candidate_vtx,
|
||||||
g_candidate_vtx)
|
g_candidate_vtx)
|
||||||
print_debug(" "*indent, 'grow vtx', g_candidate_vtx, ':', h_candidate_vtx)
|
print_debug('grow vtx', g_candidate_vtx, ':', h_candidate_vtx, id(g_candidate_vtx), id(h_candidate_vtx))
|
||||||
yield from self._match(new_state, already_visited, indent+1)
|
yield from self._match(new_state, already_visited, indent+1)
|
||||||
|
print_debug('backtrack vtx', g_candidate_vtx, ':', h_candidate_vtx, id(g_candidate_vtx), id(h_candidate_vtx))
|
||||||
|
|
||||||
print_debug(" "*indent, 'preferred...')
|
print_debug('preferred...')
|
||||||
yield from attempt_grow('outgoing', indent+1)
|
yield from attempt_grow('outgoing', indent+1)
|
||||||
yield from attempt_grow('incoming', indent+1)
|
yield from attempt_grow('incoming', indent+1)
|
||||||
|
|
||||||
print_debug(" "*indent, 'least preferred...')
|
print_debug('least preferred...')
|
||||||
for g_candidate_vtx in state.g_unmatched_vtxs:
|
if state.boundary != None:
|
||||||
|
g_boundary_vtx, _ = state.boundary
|
||||||
|
guest_boundary_component = self.guest_vtx_to_component[g_boundary_vtx]
|
||||||
|
# only try guest vertices that are in a different component (all vertices in the same component are already discovered via 'attempt_grow')
|
||||||
|
guest_components_to_try = (c for i,c in enumerate(self.guest_component_to_vtxs) if i != guest_boundary_component)
|
||||||
|
# for the host vertices however, we have to try them from all components, because different connected components of our pattern (=guest) could be mapped onto the same connected component in the host
|
||||||
|
else:
|
||||||
|
guest_components_to_try = self.guest_component_to_vtxs
|
||||||
|
|
||||||
|
for g_candidate_vtxs in guest_components_to_try:
|
||||||
|
for g_candidate_vtx in g_candidate_vtxs:
|
||||||
|
if g_candidate_vtx in state.mapping_vtxs:
|
||||||
|
print_debug("skip (already matched)", g_candidate_vtx)
|
||||||
|
continue
|
||||||
for h_candidate_vtx in state.h_unmatched_vtxs:
|
for h_candidate_vtx in state.h_unmatched_vtxs:
|
||||||
yield from attempt_match_vtxs(state, g_candidate_vtx, h_candidate_vtx, indent+1)
|
yield from attempt_match_vtxs(state, g_candidate_vtx, h_candidate_vtx, indent+1)
|
||||||
|
|
||||||
|
|
|
||||||
156
pattern_matching/mvs_adapter.py
Normal file
156
pattern_matching/mvs_adapter.py
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
from state.base import State
|
||||||
|
from uuid import UUID
|
||||||
|
from services.bottom.V0 import Bottom
|
||||||
|
from pattern_matching.matcher import Graph, Edge, Vertex
|
||||||
|
import itertools
|
||||||
|
import re
|
||||||
|
|
||||||
|
from util.timer import Timer
|
||||||
|
|
||||||
|
class _is_edge:
|
||||||
|
def __repr__(self):
|
||||||
|
return "EDGE"
|
||||||
|
# just a unique symbol that is only equal to itself
|
||||||
|
IS_EDGE = _is_edge()
|
||||||
|
|
||||||
|
class IS_TYPE:
|
||||||
|
def __init__(self, type):
|
||||||
|
# mvs-node of the type
|
||||||
|
self.type = type
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"TYPE({str(self.type)[-4:]})"
|
||||||
|
|
||||||
|
# def __eq__(self, other):
|
||||||
|
# if not isinstance(other, IS_TYPE):
|
||||||
|
# return False
|
||||||
|
# return other.type == self.type
|
||||||
|
|
||||||
|
# def __hash__(self):
|
||||||
|
# return self.type.__hash__()
|
||||||
|
|
||||||
|
|
||||||
|
UUID_REGEX = re.compile(r"[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]-[0-9a-z][0-9a-z][0-9a-z][0-9a-z]-[0-9a-z][0-9a-z][0-9a-z][0-9a-z]-[0-9a-z][0-9a-z][0-9a-z][0-9a-z]-[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]")
|
||||||
|
|
||||||
|
|
||||||
|
# Converts an object/class diagram in MVS state to the pattern matcher graph type
|
||||||
|
# ModelRefs are flattened
|
||||||
|
def model_to_graph(state: State, model: UUID):
|
||||||
|
with Timer("model_to_graph"):
|
||||||
|
bottom = Bottom(state)
|
||||||
|
|
||||||
|
graph = Graph()
|
||||||
|
|
||||||
|
mvs_edges = []
|
||||||
|
modelrefs = {}
|
||||||
|
def extract_modelref(el):
|
||||||
|
value = bottom.read_value(el)
|
||||||
|
# If the value of the el is a ModelRef (only way to detect this is to match a regex - not very clean), then extract it. We'll create a link to the referred model later.
|
||||||
|
if bottom.is_edge(el):
|
||||||
|
mvs_edges.append(el)
|
||||||
|
return IS_EDGE
|
||||||
|
if isinstance(value, str):
|
||||||
|
if UUID_REGEX.match(value) != None:
|
||||||
|
# side-effect
|
||||||
|
modelrefs[el] = UUID(value)
|
||||||
|
return None
|
||||||
|
return value
|
||||||
|
|
||||||
|
# MVS-Nodes become vertices
|
||||||
|
uuid_to_vtx = { node: Vertex(value=extract_modelref(node)) for node in bottom.read_outgoing_elements(model) }
|
||||||
|
graph.vtxs = [ vtx for vtx in uuid_to_vtx.values() ]
|
||||||
|
|
||||||
|
# For every MSV-Edge, two edges are created (for src and tgt)
|
||||||
|
for mvs_edge in mvs_edges:
|
||||||
|
mvs_src = bottom.read_edge_source(mvs_edge)
|
||||||
|
if mvs_src in uuid_to_vtx:
|
||||||
|
graph.edges.append(Edge(
|
||||||
|
src=uuid_to_vtx[mvs_src],
|
||||||
|
tgt=uuid_to_vtx[mvs_edge],
|
||||||
|
label="outgoing"))
|
||||||
|
mvs_tgt = bottom.read_edge_target(mvs_edge)
|
||||||
|
if mvs_tgt in uuid_to_vtx:
|
||||||
|
graph.edges.append(Edge(
|
||||||
|
src=uuid_to_vtx[mvs_tgt],
|
||||||
|
tgt=uuid_to_vtx[mvs_edge],
|
||||||
|
label="tgt"))
|
||||||
|
|
||||||
|
|
||||||
|
for node, ref in modelrefs.items():
|
||||||
|
# Recursively convert ref'ed model to graph
|
||||||
|
ref_model = model_to_graph(state, ref)
|
||||||
|
|
||||||
|
# Flatten and create link to ref'ed model
|
||||||
|
graph.vtxs += ref_model.vtxs
|
||||||
|
graph.edges += ref_model.edges
|
||||||
|
graph.edges.append(Edge(
|
||||||
|
src=uuid_to_vtx[node],
|
||||||
|
tgt=ref_model.vtxs[0], # which node to link to?? dirty
|
||||||
|
label="modelref"))
|
||||||
|
|
||||||
|
# # Add typing information
|
||||||
|
# for i,node in enumerate(bottom.read_outgoing_elements(model)):
|
||||||
|
# type_node, = bottom.read_outgoing_elements(node, "Morphism")
|
||||||
|
# print('node', node, 'has type', type_node)
|
||||||
|
# # We create a Vertex storing the type
|
||||||
|
# type_vertex = Vertex(value=IS_TYPE(type_node))
|
||||||
|
# graph.vtxs.append(type_vertex)
|
||||||
|
# type_edge = Edge(
|
||||||
|
# src=uuid_to_vtx[node],
|
||||||
|
# tgt=type_vertex,
|
||||||
|
# label="type")
|
||||||
|
# print(type_edge)
|
||||||
|
# graph.edges.append(type_edge)
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
# Function object for pattern matching. Decides whether to match host and guest vertices, where guest is a RAMified instance (e.g., the attributes are all strings with Python expressions), and the host is an instance (=object diagram) of the original model (=class diagram)
|
||||||
|
class RAMCompare:
|
||||||
|
def __init__(self, bottom):
|
||||||
|
self.bottom = bottom
|
||||||
|
|
||||||
|
type_model_id = bottom.state.read_dict(bottom.state.read_root(), "SCD")
|
||||||
|
self.scd_model = UUID(bottom.state.read_value(type_model_id))
|
||||||
|
|
||||||
|
def is_subtype_of(self, supposed_subtype: UUID, supposed_supertype: UUID):
|
||||||
|
inheritance_node, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance")
|
||||||
|
|
||||||
|
if supposed_subtype == supposed_supertype:
|
||||||
|
# reflexive:
|
||||||
|
return True
|
||||||
|
|
||||||
|
for outgoing in self.bottom.read_outgoing_edges(supposed_subtype):
|
||||||
|
if inheritance_node in self.bottom.read_outgoing_elements(outgoing, "Morphism"):
|
||||||
|
# 'outgoing' is an inheritance link
|
||||||
|
supertype = self.bottom.read_edge_target(outgoing)
|
||||||
|
if supertype != supposed_subtype:
|
||||||
|
if self.is_subtype_of(supertype, supposed_supertype):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __call__(self, g_val, h_val):
|
||||||
|
if g_val == None:
|
||||||
|
return h_val == None
|
||||||
|
|
||||||
|
# mvs-edges (which are converted to vertices) only match with mvs-edges
|
||||||
|
if g_val == IS_EDGE:
|
||||||
|
return h_val == IS_EDGE
|
||||||
|
|
||||||
|
if h_val == IS_EDGE:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# types only match with their supertypes
|
||||||
|
if isinstance(g_val, IS_TYPE):
|
||||||
|
if not isinstance(h_val, IS_TYPE):
|
||||||
|
return False
|
||||||
|
g_val_original_type = self.bottom.read_outgoing_elements(g_val.type, "RAMifies")
|
||||||
|
result = self.is_subtype_of(h_val.type, g_val_original_type)
|
||||||
|
print("RESULT", result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
if isinstance(h_val, IS_TYPE):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# print(g_val, h_val)
|
||||||
|
return eval(g_val, {}, {'v': h_val})
|
||||||
|
|
@ -81,6 +81,9 @@ class Bottom:
|
||||||
result = self.state.read_edge(edge)
|
result = self.state.read_edge(edge)
|
||||||
return result[1] if result != None else result
|
return result[1] if result != None else result
|
||||||
|
|
||||||
|
def is_edge(self, elem: UUID) -> bool:
|
||||||
|
return self.state.is_edge(elem)
|
||||||
|
|
||||||
def read_incoming_edges(self, target: UUID, label=None) -> List[UUID]:
|
def read_incoming_edges(self, target: UUID, label=None) -> List[UUID]:
|
||||||
"""
|
"""
|
||||||
Reads incoming edges of an element. Optionally, filter them based on their label
|
Reads incoming edges of an element. Optionally, filter them based on their label
|
||||||
|
|
|
||||||
|
|
@ -59,16 +59,26 @@ class OD:
|
||||||
# An attribute-link is indistinguishable from an ordinary link:
|
# An attribute-link is indistinguishable from an ordinary link:
|
||||||
return self.create_link(attr_link_name, object_name, target_name)
|
return self.create_link(attr_link_name, object_name, target_name)
|
||||||
|
|
||||||
def create_integer_value(self, value: int):
|
def create_integer_value(self, name: str, value: int):
|
||||||
from services.primitives.integer_type import Integer
|
from services.primitives.integer_type import Integer
|
||||||
int_node = self.bottom.create_node()
|
int_node = self.bottom.create_node()
|
||||||
integer_t = Integer(int_node, self.bottom.state)
|
integer_t = Integer(int_node, self.bottom.state)
|
||||||
integer_t.create(value)
|
integer_t.create(value)
|
||||||
name = 'int'+str(value) # name of the ref to the created integer
|
# name = 'int'+str(value) # name of the ref to the created integer
|
||||||
# By convention, the type model must have a ModelRef named "Integer"
|
# By convention, the type model must have a ModelRef named "Integer"
|
||||||
self.create_model_ref(name, "Integer", int_node)
|
self.create_model_ref(name, "Integer", int_node)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def create_string_value(self, name: str, value: str):
|
||||||
|
from services.primitives.string_type import String
|
||||||
|
string_node = self.bottom.create_node()
|
||||||
|
string_t = String(string_node, self.bottom.state)
|
||||||
|
string_t.create(value)
|
||||||
|
# name = 'str-'+value # name of the ref to the created integer
|
||||||
|
# By convention, the type model must have a ModelRef named "Integer"
|
||||||
|
self.create_model_ref(name, "String", string_node)
|
||||||
|
return name
|
||||||
|
|
||||||
# Identical to the same SCD method:
|
# Identical to the same SCD method:
|
||||||
def create_model_ref(self, name: str, type_name: str, model: UUID):
|
def create_model_ref(self, name: str, type_name: str, model: UUID):
|
||||||
# create element + morphism links
|
# create element + morphism links
|
||||||
|
|
@ -79,6 +89,7 @@ class OD:
|
||||||
|
|
||||||
|
|
||||||
def create_link(self, assoc_name: str, src_obj_name: str, tgt_obj_name: str):
|
def create_link(self, assoc_name: str, src_obj_name: str, tgt_obj_name: str):
|
||||||
|
print(tgt_obj_name)
|
||||||
src_obj_node, = self.bottom.read_outgoing_elements(self.model, src_obj_name)
|
src_obj_node, = self.bottom.read_outgoing_elements(self.model, src_obj_name)
|
||||||
tgt_obj_node, = self.bottom.read_outgoing_elements(self.model, tgt_obj_name)
|
tgt_obj_node, = self.bottom.read_outgoing_elements(self.model, tgt_obj_name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@ class SCD:
|
||||||
if max_c != None:
|
if max_c != None:
|
||||||
set_cardinality("upper", max_c)
|
set_cardinality("upper", max_c)
|
||||||
|
|
||||||
|
return class_node
|
||||||
|
|
||||||
def create_association(self, name: str, source: str, target: str,
|
def create_association(self, name: str, source: str, target: str,
|
||||||
src_min_c: int = None, src_max_c: int = None,
|
src_min_c: int = None, src_max_c: int = None,
|
||||||
tgt_min_c: int = None, tgt_max_c: int = None):
|
tgt_min_c: int = None, tgt_max_c: int = None):
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,9 @@ class PyState(State):
|
||||||
else:
|
else:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
def is_edge(self, elem: Element) -> bool:
|
||||||
|
return elem in self.edges
|
||||||
|
|
||||||
def read_dict(self, elem: Element, value: Any) -> Optional[Element]:
|
def read_dict(self, elem: Element, value: Any) -> Optional[Element]:
|
||||||
e = self.read_dict_edge(elem, value)
|
e = self.read_dict_edge(elem, value)
|
||||||
if e == None:
|
if e == None:
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,10 @@ def ramify(state: State, model: UUID) -> UUID:
|
||||||
# - max-card: same as original
|
# - max-card: same as original
|
||||||
upper_card = find_cardinality(class_node, class_upper_card_node)
|
upper_card = find_cardinality(class_node, class_upper_card_node)
|
||||||
print('creating class', class_name, "with card 0 ..", upper_card)
|
print('creating class', class_name, "with card 0 ..", upper_card)
|
||||||
ramified_scd.create_class(class_name, abstract=None, max_c=upper_card)
|
ramified_class = ramified_scd.create_class(class_name, abstract=None, max_c=upper_card)
|
||||||
|
|
||||||
|
# traceability link
|
||||||
|
bottom.create_edge(ramified_class, class_node, "RAMifies")
|
||||||
|
|
||||||
for (attr_name, attr_type) in get_attributes(class_node):
|
for (attr_name, attr_type) in get_attributes(class_node):
|
||||||
print(' creating attribute', attr_name, "with type String")
|
print(' creating attribute', attr_name, "with type String")
|
||||||
|
|
|
||||||
10
util/timer.py
Normal file
10
util/timer.py
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import time
|
||||||
|
|
||||||
|
class Timer:
|
||||||
|
def __init__(self, text):
|
||||||
|
self.text = text
|
||||||
|
def __enter__(self):
|
||||||
|
self.start_time = time.perf_counter_ns()
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
self.end_time = time.perf_counter_ns()
|
||||||
|
print(self.text, (self.end_time - self.start_time)/1000000, "ms")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue