Move function for making actions pure to simulator lib
This commit is contained in:
parent
c738e8bcd1
commit
043f44d163
2 changed files with 63 additions and 52 deletions
|
|
@ -5,9 +5,10 @@ import functools
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from framework.conformance import Conformance, render_conformance_check_result
|
from framework.conformance import Conformance, render_conformance_check_result
|
||||||
|
|
||||||
from concrete_syntax.common import indent
|
from concrete_syntax.common import indent
|
||||||
from concrete_syntax.textual_od.renderer import render_od
|
from concrete_syntax.textual_od.renderer import render_od
|
||||||
|
from transformation.cloner import clone_od
|
||||||
|
from api.od import ODAPI
|
||||||
|
|
||||||
class DecisionMaker:
|
class DecisionMaker:
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
|
|
@ -37,6 +38,7 @@ class Simulator:
|
||||||
# Run simulation until termination condition satisfied
|
# Run simulation until termination condition satisfied
|
||||||
def run(self, od):
|
def run(self, od):
|
||||||
self.__print("Start simulation")
|
self.__print("Start simulation")
|
||||||
|
self.__print(f"Decision maker: {self.decision_maker}")
|
||||||
step_counter = 0
|
step_counter = 0
|
||||||
while True:
|
while True:
|
||||||
self.__print("--------------")
|
self.__print("--------------")
|
||||||
|
|
@ -68,12 +70,23 @@ class Simulator:
|
||||||
self.__print(f"Executed {step_counter} steps.")
|
self.__print(f"Executed {step_counter} steps.")
|
||||||
return od
|
return od
|
||||||
|
|
||||||
|
def make_actions_pure(actions, od):
|
||||||
|
# Copy model before modifying it
|
||||||
|
def exec_pure(action, od):
|
||||||
|
cloned_rt_m = clone_od(od.state, od.m, od.mm)
|
||||||
|
new_od = ODAPI(od.state, cloned_rt_m, od.mm)
|
||||||
|
msgs = action(new_od)
|
||||||
|
return (new_od, msgs)
|
||||||
|
|
||||||
def filter_valid_actions(actions):
|
for descr, action in actions:
|
||||||
|
yield (descr, functools.partial(exec_pure, action, od))
|
||||||
|
|
||||||
|
|
||||||
|
def filter_valid_actions(pure_actions):
|
||||||
result = {}
|
result = {}
|
||||||
def make_tuple(new_od, msgs):
|
def make_tuple(new_od, msgs):
|
||||||
return (new_od, msgs)
|
return (new_od, msgs)
|
||||||
for name, callback in actions:
|
for name, callback in pure_actions:
|
||||||
print(f"attempt '{name}' ...", end='\r')
|
print(f"attempt '{name}' ...", end='\r')
|
||||||
(new_od, msgs) = callback()
|
(new_od, msgs) = callback()
|
||||||
conf = Conformance(new_od.state, new_od.m, new_od.mm)
|
conf = Conformance(new_od.state, new_od.m, new_od.mm)
|
||||||
|
|
@ -87,8 +100,12 @@ def filter_valid_actions(actions):
|
||||||
|
|
||||||
class RandomDecisionMaker(DecisionMaker):
|
class RandomDecisionMaker(DecisionMaker):
|
||||||
def __init__(self, seed=0, verbose=True):
|
def __init__(self, seed=0, verbose=True):
|
||||||
|
self.seed = seed
|
||||||
self.r = random.Random(seed)
|
self.r = random.Random(seed)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"RandomDecisionMaker(seed={self.seed})"
|
||||||
|
|
||||||
def __call__(self, actions):
|
def __call__(self, actions):
|
||||||
arr = [action for descr, action in actions]
|
arr = [action for descr, action in actions]
|
||||||
i = math.floor(self.r.random()*len(arr))
|
i = math.floor(self.r.random()*len(arr))
|
||||||
|
|
@ -98,6 +115,9 @@ class InteractiveDecisionMaker(DecisionMaker):
|
||||||
def __init__(self, msg="Select action:"):
|
def __init__(self, msg="Select action:"):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"InteractiveDecisionMaker()"
|
||||||
|
|
||||||
def __call__(self, actions):
|
def __call__(self, actions):
|
||||||
arr = []
|
arr = []
|
||||||
for i, (key, result) in enumerate(actions):
|
for i, (key, result) in enumerate(actions):
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,15 @@ from concrete_syntax.textual_od import parser, renderer
|
||||||
from concrete_syntax.common import indent
|
from concrete_syntax.common import indent
|
||||||
from concrete_syntax.plantuml import renderer as plantuml
|
from concrete_syntax.plantuml import renderer as plantuml
|
||||||
from util import prompt
|
from util import prompt
|
||||||
from transformation.cloner import clone_od
|
|
||||||
from api.od import ODAPI
|
from api.od import ODAPI
|
||||||
|
|
||||||
from examples.semantics.operational.simulator import Simulator, RandomDecisionMaker, InteractiveDecisionMaker, filter_valid_actions
|
from examples.semantics.operational.simulator import Simulator, RandomDecisionMaker, InteractiveDecisionMaker, make_actions_pure, filter_valid_actions
|
||||||
|
|
||||||
|
|
||||||
state = DevState()
|
state = DevState()
|
||||||
|
scd_mmm = bootstrap_scd(state) # Load meta-meta-model
|
||||||
|
|
||||||
# Load meta-meta-model
|
### Load (meta-)models ###
|
||||||
scd_mmm = bootstrap_scd(state)
|
|
||||||
|
|
||||||
# Design meta-model
|
# Design meta-model
|
||||||
woods_mm_cs = """
|
woods_mm_cs = """
|
||||||
|
|
@ -46,15 +46,6 @@ woods_mm_cs = """
|
||||||
target_lower_cardinality = 1;
|
target_lower_cardinality = 1;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
woods_mm = parser.parse_od(
|
|
||||||
state,
|
|
||||||
m_text=woods_mm_cs,
|
|
||||||
mm=scd_mmm)
|
|
||||||
|
|
||||||
conf = Conformance(state, woods_mm, scd_mmm)
|
|
||||||
print("MM ...", render_conformance_check_result(conf.check_nominal()))
|
|
||||||
|
|
||||||
# Runtime meta-model
|
# Runtime meta-model
|
||||||
woods_rt_mm_cs = woods_mm_cs + """
|
woods_rt_mm_cs = woods_mm_cs + """
|
||||||
AnimalState:Class {
|
AnimalState:Class {
|
||||||
|
|
@ -137,6 +128,14 @@ woods_rt_mm_cs = woods_mm_cs + """
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
woods_mm = parser.parse_od(
|
||||||
|
state,
|
||||||
|
m_text=woods_mm_cs,
|
||||||
|
mm=scd_mmm)
|
||||||
|
|
||||||
|
conf = Conformance(state, woods_mm, scd_mmm)
|
||||||
|
print("MM ...", render_conformance_check_result(conf.check_nominal()))
|
||||||
|
|
||||||
woods_rt_mm = parser.parse_od(
|
woods_rt_mm = parser.parse_od(
|
||||||
state,
|
state,
|
||||||
m_text=woods_rt_mm_cs,
|
m_text=woods_rt_mm_cs,
|
||||||
|
|
@ -145,14 +144,6 @@ woods_rt_mm = parser.parse_od(
|
||||||
conf = Conformance(state, woods_rt_mm, scd_mmm)
|
conf = Conformance(state, woods_rt_mm, scd_mmm)
|
||||||
print("RT-MM ...", render_conformance_check_result(conf.check_nominal()))
|
print("RT-MM ...", render_conformance_check_result(conf.check_nominal()))
|
||||||
|
|
||||||
# print("--------------")
|
|
||||||
# print(indent(
|
|
||||||
# renderer.render_od(state,
|
|
||||||
# m_id=woods_rt_mm,
|
|
||||||
# mm_id=scd_mmm),
|
|
||||||
# 4))
|
|
||||||
# print("--------------")
|
|
||||||
|
|
||||||
# Our design model - the part that doesn't change
|
# Our design model - the part that doesn't change
|
||||||
woods_m_cs = """
|
woods_m_cs = """
|
||||||
george:Man {
|
george:Man {
|
||||||
|
|
@ -174,14 +165,6 @@ woods_m_cs = """
|
||||||
:afraidOf (george -> bill)
|
:afraidOf (george -> bill)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
woods_m = parser.parse_od(
|
|
||||||
state,
|
|
||||||
m_text=woods_m_cs,
|
|
||||||
mm=woods_mm)
|
|
||||||
|
|
||||||
conf = Conformance(state, woods_m, woods_mm)
|
|
||||||
print("M ...", render_conformance_check_result(conf.check_nominal()))
|
|
||||||
|
|
||||||
# Our runtime model - the part that changes with every execution step
|
# Our runtime model - the part that changes with every execution step
|
||||||
woods_rt_initial_m_cs = woods_m_cs + """
|
woods_rt_initial_m_cs = woods_m_cs + """
|
||||||
georgeState:ManState {
|
georgeState:ManState {
|
||||||
|
|
@ -211,6 +194,14 @@ woods_rt_initial_m_cs = woods_m_cs + """
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
woods_m = parser.parse_od(
|
||||||
|
state,
|
||||||
|
m_text=woods_m_cs,
|
||||||
|
mm=woods_mm)
|
||||||
|
|
||||||
|
conf = Conformance(state, woods_m, woods_mm)
|
||||||
|
print("M ...", render_conformance_check_result(conf.check_nominal()))
|
||||||
|
|
||||||
woods_rt_m = parser.parse_od(
|
woods_rt_m = parser.parse_od(
|
||||||
state,
|
state,
|
||||||
m_text=woods_rt_initial_m_cs,
|
m_text=woods_rt_initial_m_cs,
|
||||||
|
|
@ -219,6 +210,10 @@ woods_rt_m = parser.parse_od(
|
||||||
conf = Conformance(state, woods_rt_m, woods_rt_mm)
|
conf = Conformance(state, woods_rt_m, woods_rt_mm)
|
||||||
print("RT-M ...", render_conformance_check_result(conf.check_nominal()))
|
print("RT-M ...", render_conformance_check_result(conf.check_nominal()))
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
### Semantics ###
|
||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
def state_of(od, animal):
|
def state_of(od, animal):
|
||||||
|
|
@ -229,7 +224,8 @@ def get_time(od):
|
||||||
_, clock = od.get_all_instances("Clock")[0]
|
_, clock = od.get_all_instances("Clock")[0]
|
||||||
return clock, od.get_slot_value(clock, "time")
|
return clock, od.get_slot_value(clock, "time")
|
||||||
|
|
||||||
def advance_time(od):
|
# Action: Time advances, whoever is being attacked dies, bears become hungrier
|
||||||
|
def action_advance_time(od):
|
||||||
msgs = []
|
msgs = []
|
||||||
clock, old_time = get_time(od)
|
clock, old_time = get_time(od)
|
||||||
new_time = old_time + 1
|
new_time = old_time + 1
|
||||||
|
|
@ -259,8 +255,9 @@ def advance_time(od):
|
||||||
msgs.append(f"Bear {bear_name}'s hunger level is now {new_hunger}.")
|
msgs.append(f"Bear {bear_name}'s hunger level is now {new_hunger}.")
|
||||||
return msgs
|
return msgs
|
||||||
|
|
||||||
# we must use the names of the objects as parameters, because when cloning, the IDs of objects change!
|
# Action: Animal attacks Man
|
||||||
def attack(od, animal_name: str, man_name: str):
|
# Note: We must use the names of the objects as parameters, because when cloning, the IDs of objects change!
|
||||||
|
def action_attack(od, animal_name: str, man_name: str):
|
||||||
msgs = []
|
msgs = []
|
||||||
animal = od.get(animal_name)
|
animal = od.get(animal_name)
|
||||||
man = od.get(man_name)
|
man = od.get(man_name)
|
||||||
|
|
@ -274,11 +271,11 @@ def attack(od, animal_name: str, man_name: str):
|
||||||
msgs.append(f"{animal_name} is now attacking {man_name}")
|
msgs.append(f"{animal_name} is now attacking {man_name}")
|
||||||
return msgs
|
return msgs
|
||||||
|
|
||||||
|
# Get all actions that can be performed (including those that bring us to a non-conforming state)
|
||||||
def get_all_actions(od):
|
def get_all_actions(od):
|
||||||
def _get_actions(od):
|
def _generate_actions(od):
|
||||||
# can always advance time:
|
# can always advance time:
|
||||||
yield ("advance time", advance_time)
|
yield ("advance time", action_advance_time)
|
||||||
|
|
||||||
# who can attack whom?
|
# who can attack whom?
|
||||||
for _, afraid_link in od.get_all_instances("afraidOf"):
|
for _, afraid_link in od.get_all_instances("afraidOf"):
|
||||||
|
|
@ -289,21 +286,15 @@ def get_all_actions(od):
|
||||||
man_state = state_of(od, man)
|
man_state = state_of(od, man)
|
||||||
animal_state = state_of(od, animal)
|
animal_state = state_of(od, animal)
|
||||||
descr = f"{animal_name} ({od.get_type_name(animal)}) attacks {man_name} ({od.get_type_name(man)})"
|
descr = f"{animal_name} ({od.get_type_name(animal)}) attacks {man_name} ({od.get_type_name(man)})"
|
||||||
yield (descr, functools.partial(attack, animal_name=animal_name, man_name=man_name))
|
yield (descr, functools.partial(action_attack, animal_name=animal_name, man_name=man_name))
|
||||||
|
|
||||||
# Copy model before modifying it
|
|
||||||
def exec_pure(action, od):
|
|
||||||
cloned_rt_m = clone_od(state, od.m, od.mm)
|
|
||||||
new_od = ODAPI(state, cloned_rt_m, od.mm)
|
|
||||||
msgs = action(new_od)
|
|
||||||
return (new_od, msgs)
|
|
||||||
|
|
||||||
for descr, action in _get_actions(od):
|
return make_actions_pure(_generate_actions(od), od)
|
||||||
yield (descr, functools.partial(exec_pure, action, od))
|
|
||||||
|
|
||||||
|
# Only get those actions that bring us to a conforming state
|
||||||
def get_valid_actions(od):
|
def get_valid_actions(od):
|
||||||
return filter_valid_actions(get_all_actions(od))
|
return filter_valid_actions(get_all_actions(od))
|
||||||
|
|
||||||
|
# Render our run-time state to a string
|
||||||
def render_woods(od):
|
def render_woods(od):
|
||||||
txt = ""
|
txt = ""
|
||||||
_, time = get_time(od)
|
_, time = get_time(od)
|
||||||
|
|
@ -336,7 +327,7 @@ def render_woods(od):
|
||||||
txt += f" 👨 {od.get_name(man)} ({render_dead(man_state)}) {render_attacking(man_state)}{being_attacked}\n"
|
txt += f" 👨 {od.get_name(man)} ({render_dead(man_state)}) {render_attacking(man_state)}{being_attacked}\n"
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
|
# When should simulation stop?
|
||||||
def termination_condition(od):
|
def termination_condition(od):
|
||||||
_, time = get_time(od)
|
_, time = get_time(od)
|
||||||
if time >= 10:
|
if time >= 10:
|
||||||
|
|
@ -351,18 +342,18 @@ def termination_condition(od):
|
||||||
if len(who_is_dead) >= 2:
|
if len(who_is_dead) >= 2:
|
||||||
return f"{' and '.join(who_is_dead)} are dead"
|
return f"{' and '.join(who_is_dead)} are dead"
|
||||||
|
|
||||||
|
|
||||||
sim = Simulator(
|
sim = Simulator(
|
||||||
action_generator=get_valid_actions,
|
action_generator=get_valid_actions,
|
||||||
# action_generator=get_actions,
|
# action_generator=get_all_actions,
|
||||||
decision_maker=RandomDecisionMaker(seed=0),
|
decision_maker=RandomDecisionMaker(seed=0),
|
||||||
# decision_maker=InteractiveDecisionMaker(),
|
# decision_maker=InteractiveDecisionMaker(),
|
||||||
termination_condition=termination_condition,
|
termination_condition=termination_condition,
|
||||||
check_conformance=True,
|
check_conformance=False,
|
||||||
verbose=True,
|
verbose=True,
|
||||||
renderer=render_woods,
|
renderer=render_woods,
|
||||||
)
|
)
|
||||||
|
|
||||||
od = ODAPI(state, woods_rt_m, woods_rt_mm)
|
od = ODAPI(state, woods_rt_m, woods_rt_mm)
|
||||||
|
|
||||||
print()
|
|
||||||
sim.run(od)
|
sim.run(od)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue