Example of rendering the entire match set, and rendering the rewrite (match between RHS and updated host graph)

This commit is contained in:
Joeri Exelmans 2024-09-13 12:09:24 +02:00
parent a926de1998
commit 13f19cdec6
4 changed files with 174 additions and 62 deletions

View file

@ -0,0 +1,36 @@
from state.devstate import DevState
from bootstrap.scd import bootstrap_scd
from uuid import UUID
from services.scd import SCD
from framework.conformance import Conformance
from services.od import OD
from transformation.ramify import ramify
from transformation import rewriter
from services.bottom.V0 import Bottom
from services.primitives.integer_type import Integer
from pattern_matching import mvs_adapter
from pattern_matching.matcher import MatcherVF2
from renderer import plantuml
def main():
state = DevState()
root = state.read_root() # id: 0
scd_mm_id = bootstrap_scd(state)
uml = ""
# Render SCD Meta-Model as Object Diagram
uml += plantuml.render_package("Object Diagram", plantuml.render_object_diagram(state, scd_mm_id, scd_mm_id, prefix_ids="od_"))
# Render SCD Meta-Model as Class Diagram
uml += plantuml.render_package("Class Diagram", plantuml.render_class_diagram(state, scd_mm_id, prefix_ids="cd_"))
# Render conformance
uml += plantuml.render_trace_conformance(state, scd_mm_id, scd_mm_id, prefix_inst_ids="od_", prefix_type_ids="cd_")
print(uml)
if __name__ == "__main__":
main()

View file

@ -149,47 +149,91 @@ def main():
print(guest.vtxs) print(guest.vtxs)
print(guest.edges) print(guest.edges)
def render_ramification():
uml = (""
# Render original and RAMified meta-models
+ plantuml.render_package("Meta-Model", plantuml.render_class_diagram(state, dsl_mm_id))
+ plantuml.render_package("RAMified Meta-Model", plantuml.render_class_diagram(state, ramified_mm_id))
# Render RAMification traceability links
+ plantuml.render_trace_ramifies(state, dsl_mm_id, ramified_mm_id)
)
# Render pattern
uml += plantuml.render_package("LHS", plantuml.render_object_diagram(state, lhs_id, ramified_mm_id))
uml += plantuml.render_trace_conformance(state, lhs_id, ramified_mm_id)
# Render pattern
uml += plantuml.render_package("RHS", plantuml.render_object_diagram(state, rhs_id, ramified_mm_id))
uml += plantuml.render_trace_conformance(state, rhs_id, ramified_mm_id)
return uml
def render_all_matches():
uml = render_ramification()
# Render host graph (before rewriting)
uml += plantuml.render_package("Model (before rewrite)", plantuml.render_object_diagram(state, dsl_m_id, dsl_mm_id))
# Render conformance
uml += plantuml.render_trace_conformance(state, dsl_m_id, dsl_mm_id)
print("matching...") print("matching...")
matcher = MatcherVF2(host, guest, mvs_adapter.RAMCompare(Bottom(state), dsl_m_od)) matcher = MatcherVF2(host, guest, mvs_adapter.RAMCompare(Bottom(state), dsl_m_od))
for m in matcher.match(): for m, color in zip(matcher.match(), ["red", "orange"]):
print("\nMATCH:\n", m) print("\nMATCH:\n", m)
name_mapping = {} name_mapping = {}
id_mapping = {} # id_mapping = {}
for guest_vtx, host_vtx in m.mapping_vtxs.items(): for guest_vtx, host_vtx in m.mapping_vtxs.items():
if isinstance(guest_vtx, mvs_adapter.NamedNode) and isinstance(host_vtx, mvs_adapter.NamedNode): if isinstance(guest_vtx, mvs_adapter.NamedNode) and isinstance(host_vtx, mvs_adapter.NamedNode):
id_mapping[guest_vtx.node_id] = host_vtx.node_id # id_mapping[guest_vtx.node_id] = host_vtx.node_id
name_mapping[guest_vtx.name] = host_vtx.name name_mapping[guest_vtx.name] = host_vtx.name
print(name_mapping) print(name_mapping)
#rewriter.rewrite(state, lhs_id, rhs_id, ramified_mm_id, name_mapping, dsl_m_id, dsl_mm_id)
break # Render every match
uml += plantuml.render_trace_match(state, name_mapping, lhs_id, dsl_m_id, color)
print("DONE") print("DONE")
return uml
def render_rewrite():
uml = render_ramification()
matcher = MatcherVF2(host, guest, mvs_adapter.RAMCompare(Bottom(state), dsl_m_od))
for m in matcher.match():
name_mapping = {}
# id_mapping = {}
for guest_vtx, host_vtx in m.mapping_vtxs.items():
if isinstance(guest_vtx, mvs_adapter.NamedNode) and isinstance(host_vtx, mvs_adapter.NamedNode):
# id_mapping[guest_vtx.node_id] = host_vtx.node_id
name_mapping[guest_vtx.name] = host_vtx.name
print(name_mapping)
rewriter.rewrite(state, lhs_id, rhs_id, ramified_mm_id, name_mapping, dsl_m_id, dsl_mm_id)
# Render match
uml_match = plantuml.render_trace_match(state, name_mapping, rhs_id, dsl_m_id)
# Stop matching after rewrite
break
# Render host graph (after rewriting)
uml += plantuml.render_package("Model (after rewrite)", plantuml.render_object_diagram(state, dsl_m_id, dsl_mm_id))
# Render conformance
uml += plantuml.render_trace_conformance(state, dsl_m_id, dsl_mm_id)
uml += uml_match
return uml
conf5 = Conformance(state, dsl_m_id, dsl_mm_id) conf5 = Conformance(state, dsl_m_id, dsl_mm_id)
print("Updated model conforms?", conf5.check_nominal(log=True)) print("Updated model conforms?", conf5.check_nominal(log=True))
print()
print("==============================================")
print(render_all_matches())
# print(render_rewrite())
# Render original and RAMified meta-models
print(plantuml.render_package("Meta-Model", plantuml.render_class_diagram(state, dsl_mm_id)))
print(plantuml.render_package("RAMified Meta-Model", plantuml.render_class_diagram(state, ramified_mm_id)))
# Render RAMification traceability links
print(plantuml.render_trace_ramifies(state, dsl_mm_id, ramified_mm_id))
# Render host graph
print(plantuml.render_package("Model", plantuml.render_object_diagram(state, dsl_m_id, dsl_mm_id)))
# Render conformance host -> MM
print(plantuml.render_trace_conformance(state, dsl_m_id, dsl_mm_id))
# Render pattern
print(plantuml.render_package("LHS", plantuml.render_object_diagram(state, lhs_id, ramified_mm_id)))
# Render pattern -> RAM-MM
print(plantuml.render_trace_conformance(state, lhs_id, ramified_mm_id))
print(plantuml.render_trace_match(state, id_mapping))
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -2,12 +2,16 @@ from services import scd, od
from services.bottom.V0 import Bottom from services.bottom.V0 import Bottom
from transformation import ramify from transformation import ramify
import json import json
from uuid import UUID
def render_class_diagram(state, model): def render_class_diagram(state, model, prefix_ids=""):
bottom = Bottom(state) bottom = Bottom(state)
model_scd = scd.SCD(model, state) model_scd = scd.SCD(model, state)
model_od = od.OD(od.get_scd_mm(bottom), model, state) model_od = od.OD(od.get_scd_mm(bottom), model, state)
def make_id(uuid) -> str:
return prefix_ids+str(uuid).replace('-','_')
output = "" output = ""
# Render classes # Render classes
@ -69,11 +73,14 @@ def render_class_diagram(state, model):
return output return output
def render_object_diagram(state, m, mm, render_attributes=True): def render_object_diagram(state, m, mm, render_attributes=True, prefix_ids=""):
bottom = Bottom(state) bottom = Bottom(state)
mm_scd = scd.SCD(mm, state) mm_scd = scd.SCD(mm, state)
m_od = od.OD(mm, m, state) m_od = od.OD(mm, m, state)
def make_id(uuid) -> str:
return prefix_ids+str(uuid).replace('-','_')
output = "" output = ""
# Render objects # Render objects
@ -101,7 +108,7 @@ def render_object_diagram(state, m, mm, render_attributes=True):
src_name = m_od.get_object_name(src_obj) src_name = m_od.get_object_name(src_obj)
tgt_name = m_od.get_object_name(tgt_obj) tgt_name = m_od.get_object_name(tgt_obj)
output += f"\n{make_id(src_obj)} -> {make_id(tgt_obj)} : {assoc_name}" output += f"\n{make_id(src_obj)} -> {make_id(tgt_obj)} : :{assoc_name}"
return output return output
@ -112,19 +119,24 @@ def render_package(name, contents):
output += '\n}' output += '\n}'
return output return output
def render_trace_ramifies(state, mm, ramified_mm, render_attributes=True): def render_trace_ramifies(state, mm, ramified_mm, render_attributes=True, prefix_ram_ids="", prefix_orig_ids=""):
bottom = Bottom(state) bottom = Bottom(state)
mm_scd = scd.SCD(mm, state) mm_scd = scd.SCD(mm, state)
ramified_mm_scd = scd.SCD(ramified_mm, state) ramified_mm_scd = scd.SCD(ramified_mm, state)
def make_ram_id(uuid) -> str:
return prefix_ram_ids+str(uuid).replace('-','_')
def make_orig_id(uuid) -> str:
return prefix_orig_ids+str(uuid).replace('-','_')
output = "" output = ""
# Render RAMifies-edges between classes # Render RAMifies-edges between classes
for ram_name, ram_class_node in ramified_mm_scd.get_classes().items(): for ram_name, ram_class_node in ramified_mm_scd.get_classes().items():
original_class, = bottom.read_outgoing_elements(ram_class_node, ramify.RAMIFIES_LABEL) original_class, = bottom.read_outgoing_elements(ram_class_node, ramify.RAMIFIES_LABEL)
original_name = mm_scd.get_class_name(original_class) original_name = mm_scd.get_class_name(original_class)
output += f"\n{make_id(ram_class_node)} ..> {make_id(original_class)} #line:green;text:green : RAMifies" output += f"\n{make_ram_id(ram_class_node)} ..> {make_orig_id(original_class)} #line:green;text:green : RAMifies"
if render_attributes: if render_attributes:
# and between attributes # and between attributes
@ -133,16 +145,21 @@ def render_trace_ramifies(state, mm, ramified_mm, render_attributes=True):
orig_class_node = bottom.read_edge_source(orig_attr_edge) orig_class_node = bottom.read_edge_source(orig_attr_edge)
# dirty AF: # dirty AF:
orig_attr_name = mm_scd.get_class_name(orig_attr_edge)[len(original_name)+1:] orig_attr_name = mm_scd.get_class_name(orig_attr_edge)[len(original_name)+1:]
output += f"\n{make_id(ram_class_node)}::{ram_attr_name} ..> {make_id(orig_class_node)}::{orig_attr_name} #line:green;text:green : RAMifies" output += f"\n{make_ram_id(ram_class_node)}::{ram_attr_name} ..> {make_orig_id(orig_class_node)}::{orig_attr_name} #line:green;text:green : RAMifies"
return output return output
def render_trace_conformance(state, m, mm, render_attributes=True): def render_trace_conformance(state, m, mm, render_attributes=True, prefix_inst_ids="", prefix_type_ids=""):
bottom = Bottom(state) bottom = Bottom(state)
mm_scd = scd.SCD(mm, state) mm_scd = scd.SCD(mm, state)
m_od = od.OD(mm, m, state) m_od = od.OD(mm, m, state)
def make_inst_id(uuid) -> str:
return prefix_inst_ids+str(uuid).replace('-','_')
def make_type_id(uuid) -> str:
return prefix_type_ids+str(uuid).replace('-','_')
output = "" output = ""
# Render objects # Render objects
@ -152,39 +169,50 @@ def render_trace_conformance(state, m, mm, render_attributes=True):
attributes = od.get_attributes(bottom, class_node) attributes = od.get_attributes(bottom, class_node)
for obj_name, obj_node in m_od.get_objects(class_node).items(): for obj_name, obj_node in m_od.get_objects(class_node).items():
output += f"\n{make_id(obj_node)} ..> {make_id(class_node)} #line:blue;text:blue : instanceOf" output += f"\n{make_inst_id(obj_node)} ..> {make_type_id(class_node)} #line:blue;text:blue : instanceOf"
if render_attributes: if render_attributes:
for attr_name, attr_edge in attributes: for attr_name, attr_edge in attributes:
slot = m_od.get_slot(obj_node, attr_name) slot = m_od.get_slot(obj_node, attr_name)
if slot != None: if slot != None:
output += f"\n{make_id(obj_node)}::{attr_name} ..> {make_id(class_node)}::{attr_name} #line:blue;text:blue : instanceOf" output += f"\n{make_inst_id(obj_node)}::{attr_name} ..> {make_type_id(class_node)}::{attr_name} #line:blue;text:blue : instanceOf"
output += '\n' output += '\n'
return output return output
def render_trace_match(state, mapping): def render_trace_match(state, name_mapping: dict, pattern_m: UUID, host_m: UUID, color="grey", prefix_pattern_ids="", prefix_host_ids=""):
bottom = Bottom(state) bottom = Bottom(state)
class_type = od.get_scd_mm_class_node(bottom) class_type = od.get_scd_mm_class_node(bottom)
attr_link_type = od.get_scd_mm_attributelink_node(bottom) attr_link_type = od.get_scd_mm_attributelink_node(bottom)
def make_pattern_id(uuid) -> str:
return prefix_pattern_ids+str(uuid).replace('-','_')
def make_host_id(uuid) -> str:
return prefix_host_ids+str(uuid).replace('-','_')
output = "" output = ""
for pattern_el, host_el in mapping.items(): render_suffix = f"#line:{color};line.dotted;text:{color} : matchedWith"
for pattern_el_name, host_el_name in name_mapping.items():
print(pattern_el_name, host_el_name)
try:
pattern_el, = bottom.read_outgoing_elements(pattern_m, pattern_el_name)
host_el, = bottom.read_outgoing_elements(host_m, host_el_name)
except:
continue
# only render 'match'-edges between objects (= those elements where the type of the type is 'Class'): # only render 'match'-edges between objects (= those elements where the type of the type is 'Class'):
pattern_el_type = od.get_type(bottom, pattern_el) pattern_el_type = od.get_type(bottom, pattern_el)
pattern_el_type_type = od.get_type(bottom, pattern_el_type) pattern_el_type_type = od.get_type(bottom, pattern_el_type)
if pattern_el_type_type == class_type: if pattern_el_type_type == class_type:
output += f"\n{make_id(pattern_el)} ..> {make_id(host_el)} #line:grey;text:grey : matchedWith" output += f"\n{make_pattern_id(pattern_el)} ..> {make_host_id(host_el)} {render_suffix}"
elif pattern_el_type_type == attr_link_type: elif pattern_el_type_type == attr_link_type:
pattern_obj = bottom.read_edge_source(pattern_el) pattern_obj = bottom.read_edge_source(pattern_el)
pattern_attr_name = od.get_attr_name(bottom, pattern_el_type) pattern_attr_name = od.get_attr_name(bottom, pattern_el_type)
host_obj = bottom.read_edge_source(host_el) host_obj = bottom.read_edge_source(host_el)
host_el_type = od.get_type(bottom, host_el) host_el_type = od.get_type(bottom, host_el)
host_attr_name = od.get_attr_name(bottom, host_el_type) host_attr_name = od.get_attr_name(bottom, host_el_type)
output += f"\n{make_id(pattern_obj)}::{pattern_attr_name} ..> {make_id(host_obj)}::{host_attr_name} #line:grey;text:grey : matchedWith" output += f"\n{make_pattern_id(pattern_obj)}::{pattern_attr_name} ..> {make_host_id(host_obj)}::{host_attr_name} {render_suffix}"
return output return output
def make_id(uuid) -> str:
return (str(uuid).replace('-','_'))

View file

@ -23,7 +23,9 @@ def process_rule(state, lhs: UUID, rhs: UUID):
return to_delete, to_create, common return to_delete, to_create, common
def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to_transform: UUID, mm: UUID) -> UUID: # Rewrite is performed in-place
# Also updates the mapping in-place, to become RHS -> host
def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to_transform: UUID, mm: UUID) -> dict:
bottom = Bottom(state) bottom = Bottom(state)
scd_metamodel_id = state.read_dict(state.read_root(), "SCD") scd_metamodel_id = state.read_dict(state.read_root(), "SCD")
@ -50,8 +52,10 @@ def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to
element_to_delete, = bottom.read_outgoing_elements(m_to_transform, model_element_name_to_delete) element_to_delete, = bottom.read_outgoing_elements(m_to_transform, model_element_name_to_delete)
# Delete # Delete
bottom.delete_element(element_to_delete) bottom.delete_element(element_to_delete)
# Remove from mapping
del match_mapping[pattern_name_to_delete]
extended_mapping = dict(match_mapping) # will be extended with created elements # extended_mapping = dict(match_mapping) # will be extended with created elements
edges_to_create = [] # postpone creation of edges after creation of nodes edges_to_create = [] # postpone creation of edges after creation of nodes
# Perform creations # Perform creations
@ -74,7 +78,7 @@ def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to
# It's type is typed by Class -> it's an object # It's type is typed by Class -> it's an object
print(' -> creating object') print(' -> creating object')
o = m_od._create_object(model_element_name_to_create, original_type) o = m_od._create_object(model_element_name_to_create, original_type)
extended_mapping[pattern_name_to_create] = model_element_name_to_create match_mapping[pattern_name_to_create] = model_element_name_to_create
elif od.is_typed_by(bottom, original_type, attr_link_type): elif od.is_typed_by(bottom, original_type, attr_link_type):
print(' -> postpone (is attribute link)') print(' -> postpone (is attribute link)')
edges_to_create.append((pattern_name_to_create, rhs_element_to_create, original_type, 'attribute link', rhs_type, model_element_name_to_create)) edges_to_create.append((pattern_name_to_create, rhs_element_to_create, original_type, 'attribute link', rhs_type, model_element_name_to_create))
@ -98,10 +102,10 @@ def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to
m_od.create_integer_value(model_element_name_to_create, result) m_od.create_integer_value(model_element_name_to_create, result)
elif isinstance(result, str): elif isinstance(result, str):
m_od.create_string_value(model_element_name_to_create, result) m_od.create_string_value(model_element_name_to_create, result)
extended_mapping[pattern_name_to_create] = model_element_name_to_create match_mapping[pattern_name_to_create] = model_element_name_to_create
print('extended_mapping:', extended_mapping) print('match_mapping:', match_mapping)
print("create edges....") print("create edges....")
for pattern_name_to_create, rhs_element_to_create, original_type, original_type_name, rhs_type, model_element_name_to_create in edges_to_create: for pattern_name_to_create, rhs_element_to_create, original_type, original_type_name, rhs_type, model_element_name_to_create in edges_to_create:
@ -112,24 +116,24 @@ def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to
src_name = od.get_object_name(bottom, rhs, src) src_name = od.get_object_name(bottom, rhs, src)
tgt = bottom.read_edge_target(rhs_element_to_create) tgt = bottom.read_edge_target(rhs_element_to_create)
tgt_name = od.get_object_name(bottom, rhs, tgt) tgt_name = od.get_object_name(bottom, rhs, tgt)
obj_name = extended_mapping[src_name] # name of object in host graph to create slot for obj_name = match_mapping[src_name] # name of object in host graph to create slot for
attr_name = od.get_object_name(bottom, mm, original_type) attr_name = od.get_object_name(bottom, mm, original_type)
class_name = m_od.get_class_of_object(obj_name) class_name = m_od.get_class_of_object(obj_name)
# Just when you thought the code couldn't get any dirtier: # Just when you thought the code couldn't get any dirtier:
attribute_name = attr_name[len(class_name)+1:] attribute_name = attr_name[len(class_name)+1:]
# print(attribute_name, obj_name, extended_mapping[tgt_name]) # print(attribute_name, obj_name, match_mapping[tgt_name])
m_od.create_slot(attribute_name, obj_name, extended_mapping[tgt_name]) m_od.create_slot(attribute_name, obj_name, match_mapping[tgt_name])
elif original_type_name == 'link': elif original_type_name == 'link':
print(' -> creating link') print(' -> creating link')
src = bottom.read_edge_source(rhs_element_to_create) src = bottom.read_edge_source(rhs_element_to_create)
src_name = od.get_object_name(bottom, rhs, src) src_name = od.get_object_name(bottom, rhs, src)
tgt = bottom.read_edge_target(rhs_element_to_create) tgt = bottom.read_edge_target(rhs_element_to_create)
tgt_name = od.get_object_name(bottom, rhs, tgt) tgt_name = od.get_object_name(bottom, rhs, tgt)
obj_name = extended_mapping[src_name] # name of object in host graph to create slot for obj_name = match_mapping[src_name] # name of object in host graph to create slot for
attr_name = od.get_object_name(bottom, mm, original_type) attr_name = od.get_object_name(bottom, mm, original_type)
class_name = m_od.get_class_of_object(obj_name) class_name = m_od.get_class_of_object(obj_name)
# print(attr_name, obj_name, extended_mapping[tgt_name]) # print(attr_name, obj_name, match_mapping[tgt_name])
m_od.create_link(model_element_name_to_create, attr_name, obj_name, extended_mapping[tgt_name]) m_od.create_link(model_element_name_to_create, attr_name, obj_name, match_mapping[tgt_name])
# Perform updates # Perform updates
for pattern_element_name in common: for pattern_element_name in common:
@ -149,7 +153,7 @@ def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to
result = eval(expr, {}, {'v': old_value}) result = eval(expr, {}, {'v': old_value})
# print('eval result=', result) # print('eval result=', result)
if isinstance(result, int): if isinstance(result, int):
# overwrite the old value # overwrite the old value, in-place
referred_model_id = UUID(bottom.read_value(model_element)) referred_model_id = UUID(bottom.read_value(model_element))
Integer(referred_model_id, state).create(result) Integer(referred_model_id, state).create(result)
else: else: