muMLE/experiments/exp_scd.py

239 lines
8.9 KiB
Python

# Simple Class Diagram experiment
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
import sys
def create_integer_node(state, i: int):
node = state.create_node()
integer_t = Integer(node, state)
integer_t.create(i)
return node
def main():
state = DevState()
root = state.read_root() # id: 0
scd_mm_id = bootstrap_scd(state)
int_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "Integer")))
string_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "String")))
# def print_tree(root, max_depth, depth=0):
# print(" "*depth, "root=", root, "value=", state.read_value(root))
# src,tgt = state.read_edge(root)
# if src != None:
# print(" "*depth, "src...")
# print_tree(src, max_depth, depth+1)
# if tgt != None:
# print(" "*depth, "tgt...")
# print_tree(tgt, max_depth, depth+1)
# for edge in state.read_outgoing(root):
# for edge_label in state.read_outgoing(edge):
# [_,tgt] = state.read_edge(edge_label)
# label = state.read_value(tgt)
# print(" "*depth, " key:", label)
# [_, tgt] = state.read_edge(edge)
# value = state.read_value(tgt)
# if value != None:
# print(" "*depth, " ->", tgt, " (value:", value, ")")
# else:
# print(" "*depth, " ->", tgt)
# if depth < max_depth:
# if isinstance(value, str) and len(value) == 36:
# i = None
# try:
# i = UUID(value)
# except ValueError as e:
# # print("invalid UUID:", value)
# pass
# if i != None:
# print_tree(i, max_depth, depth+1)
# print_tree(tgt, max_depth, depth+1)
# Meta-model for our DSL
dsl_mm_id = state.create_node()
dsl_mm_scd = SCD(dsl_mm_id, state)
dsl_mm_scd.create_class("Animal", abstract=True)
dsl_mm_scd.create_class("Man", min_c=1, max_c=2)
dsl_mm_scd.create_inheritance("Man", "Animal")
dsl_mm_scd.create_model_ref("Integer", int_mm_id)
dsl_mm_scd.create_attribute_link("Man", "Integer", "weight", optional=False)
dsl_mm_scd.create_class("Bear")
dsl_mm_scd.create_inheritance("Bear", "Animal")
dsl_mm_scd.create_association("afraidOf", "Man", "Animal",
# Every Man afraid of at least one Animal:
src_min_c=0,
src_max_c=None,
tgt_min_c=1,
tgt_max_c=None,
)
print(dsl_mm_scd.list_elements())
conf = Conformance(state, dsl_mm_id, scd_mm_id)
print("conforms?", conf.check_nominal(log=True))
# Model in our DSL
dsl_m_id = state.create_node()
dsl_m_od = OD(dsl_mm_id, dsl_m_id, state)
# dsl_m_od.create_object("animal", "Animal")
dsl_m_od.create_object("george", "Man")
dsl_m_od.create_slot("weight", "george",
dsl_m_od.create_integer_value("george.weight", 80))
# "george_weight"
dsl_m_od.create_object("bear1", "Bear")
dsl_m_od.create_object("bear2", "Bear")
dsl_m_od.create_link("georgeAfraidOfBear1", "afraidOf", "george", "bear1")
dsl_m_od.create_link("georgeAfraidOfBear2", "afraidOf", "george", "bear2")
conf2 = Conformance(state, dsl_m_id, dsl_mm_id)
print("DSL instance conforms?", conf2.check_nominal(log=True))
print(conf2.type_mapping)
# RAMify MM
prefix = "RAM_" # all ramified types can be prefixed to distinguish them a bit more
ramified_mm_id = ramify(state, dsl_mm_id, prefix)
# LHS of our rule
lhs_id = state.create_node()
lhs_od = OD(ramified_mm_id, lhs_id, state)
lhs_od.create_object("man", prefix+"Man")
lhs_od.create_slot(prefix+"weight", "man", lhs_od.create_string_value(f"man.{prefix}weight", 'v < 99'))
lhs_od.create_object("scaryAnimal", prefix+"Animal")
lhs_od.create_link("manAfraidOfAnimal", prefix+"afraidOf", "man", "scaryAnimal")
conf3 = Conformance(state, lhs_id, ramified_mm_id)
print("LHS conforms?", conf3.check_nominal(log=True))
# RHS of our rule
rhs_id = state.create_node()
rhs_od = OD(ramified_mm_id, rhs_id, state)
rhs_od.create_object("man", prefix+"Man")
rhs_od.create_slot(prefix+"weight", "man", rhs_od.create_string_value(f"man.{prefix}weight", 'v + 5'))
rhs_od.create_object("bill", prefix+"Man")
rhs_od.create_slot(prefix+"weight", "bill", rhs_od.create_string_value(f"bill.{prefix}weight", '100'))
rhs_od.create_link("billAfraidOfMan", prefix+"afraidOf", "bill", "man")
conf4 = Conformance(state, rhs_id, ramified_mm_id)
print("RHS conforms?", conf4.check_nominal(log=True))
# Convert to format understood by matching algorithm
host = mvs_adapter.model_to_graph(state, dsl_m_id, dsl_mm_id)
guest = mvs_adapter.model_to_graph(state, lhs_id, ramified_mm_id)
print("HOST:")
print(host.vtxs)
print(host.edges)
print("GUEST:")
print(guest.vtxs)
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...")
matcher = MatcherVF2(host, guest, mvs_adapter.RAMCompare(Bottom(state), dsl_m_od))
for m, color in zip(matcher.match(), ["red", "orange"]):
print("\nMATCH:\n", m)
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)
# Render every match
uml += plantuml.render_trace_match(state, name_mapping, lhs_id, dsl_m_id, color)
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)
print("Updated model conforms?", conf5.check_nominal(log=True))
print()
print("==============================================")
print(render_all_matches())
# print(render_rewrite())
if __name__ == "__main__":
main()