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
|
||||
|
||||
from framework.conformance import Conformance, render_conformance_check_result
|
||||
|
||||
from concrete_syntax.common import indent
|
||||
from concrete_syntax.textual_od.renderer import render_od
|
||||
from transformation.cloner import clone_od
|
||||
from api.od import ODAPI
|
||||
|
||||
class DecisionMaker:
|
||||
@abc.abstractmethod
|
||||
|
|
@ -37,6 +38,7 @@ class Simulator:
|
|||
# Run simulation until termination condition satisfied
|
||||
def run(self, od):
|
||||
self.__print("Start simulation")
|
||||
self.__print(f"Decision maker: {self.decision_maker}")
|
||||
step_counter = 0
|
||||
while True:
|
||||
self.__print("--------------")
|
||||
|
|
@ -68,12 +70,23 @@ class Simulator:
|
|||
self.__print(f"Executed {step_counter} steps.")
|
||||
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 = {}
|
||||
def make_tuple(new_od, msgs):
|
||||
return (new_od, msgs)
|
||||
for name, callback in actions:
|
||||
for name, callback in pure_actions:
|
||||
print(f"attempt '{name}' ...", end='\r')
|
||||
(new_od, msgs) = callback()
|
||||
conf = Conformance(new_od.state, new_od.m, new_od.mm)
|
||||
|
|
@ -87,8 +100,12 @@ def filter_valid_actions(actions):
|
|||
|
||||
class RandomDecisionMaker(DecisionMaker):
|
||||
def __init__(self, seed=0, verbose=True):
|
||||
self.seed = seed
|
||||
self.r = random.Random(seed)
|
||||
|
||||
def __str__(self):
|
||||
return f"RandomDecisionMaker(seed={self.seed})"
|
||||
|
||||
def __call__(self, actions):
|
||||
arr = [action for descr, action in actions]
|
||||
i = math.floor(self.r.random()*len(arr))
|
||||
|
|
@ -98,6 +115,9 @@ class InteractiveDecisionMaker(DecisionMaker):
|
|||
def __init__(self, msg="Select action:"):
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return f"InteractiveDecisionMaker()"
|
||||
|
||||
def __call__(self, actions):
|
||||
arr = []
|
||||
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.plantuml import renderer as plantuml
|
||||
from util import prompt
|
||||
from transformation.cloner import clone_od
|
||||
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()
|
||||
scd_mmm = bootstrap_scd(state) # Load meta-meta-model
|
||||
|
||||
# Load meta-meta-model
|
||||
scd_mmm = bootstrap_scd(state)
|
||||
### Load (meta-)models ###
|
||||
|
||||
# Design meta-model
|
||||
woods_mm_cs = """
|
||||
|
|
@ -46,15 +46,6 @@ woods_mm_cs = """
|
|||
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
|
||||
woods_rt_mm_cs = woods_mm_cs + """
|
||||
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(
|
||||
state,
|
||||
m_text=woods_rt_mm_cs,
|
||||
|
|
@ -145,14 +144,6 @@ woods_rt_mm = parser.parse_od(
|
|||
conf = Conformance(state, woods_rt_mm, scd_mmm)
|
||||
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
|
||||
woods_m_cs = """
|
||||
george:Man {
|
||||
|
|
@ -174,14 +165,6 @@ woods_m_cs = """
|
|||
: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
|
||||
woods_rt_initial_m_cs = woods_m_cs + """
|
||||
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(
|
||||
state,
|
||||
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)
|
||||
print("RT-M ...", render_conformance_check_result(conf.check_nominal()))
|
||||
|
||||
print()
|
||||
|
||||
|
||||
### Semantics ###
|
||||
|
||||
# Helpers
|
||||
def state_of(od, animal):
|
||||
|
|
@ -229,7 +224,8 @@ def get_time(od):
|
|||
_, clock = od.get_all_instances("Clock")[0]
|
||||
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 = []
|
||||
clock, old_time = get_time(od)
|
||||
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}.")
|
||||
return msgs
|
||||
|
||||
# we must use the names of the objects as parameters, because when cloning, the IDs of objects change!
|
||||
def attack(od, animal_name: str, man_name: str):
|
||||
# Action: Animal attacks Man
|
||||
# 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 = []
|
||||
animal = od.get(animal_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}")
|
||||
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_actions(od):
|
||||
def _generate_actions(od):
|
||||
# can always advance time:
|
||||
yield ("advance time", advance_time)
|
||||
yield ("advance time", action_advance_time)
|
||||
|
||||
# who can attack whom?
|
||||
for _, afraid_link in od.get_all_instances("afraidOf"):
|
||||
|
|
@ -289,21 +286,15 @@ def get_all_actions(od):
|
|||
man_state = state_of(od, man)
|
||||
animal_state = state_of(od, animal)
|
||||
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))
|
||||
|
||||
# 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)
|
||||
yield (descr, functools.partial(action_attack, animal_name=animal_name, man_name=man_name))
|
||||
|
||||
for descr, action in _get_actions(od):
|
||||
yield (descr, functools.partial(exec_pure, action, od))
|
||||
return make_actions_pure(_generate_actions(od), od)
|
||||
|
||||
# Only get those actions that bring us to a conforming state
|
||||
def get_valid_actions(od):
|
||||
return filter_valid_actions(get_all_actions(od))
|
||||
|
||||
# Render our run-time state to a string
|
||||
def render_woods(od):
|
||||
txt = ""
|
||||
_, 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"
|
||||
return txt
|
||||
|
||||
|
||||
# When should simulation stop?
|
||||
def termination_condition(od):
|
||||
_, time = get_time(od)
|
||||
if time >= 10:
|
||||
|
|
@ -351,18 +342,18 @@ def termination_condition(od):
|
|||
if len(who_is_dead) >= 2:
|
||||
return f"{' and '.join(who_is_dead)} are dead"
|
||||
|
||||
|
||||
sim = Simulator(
|
||||
action_generator=get_valid_actions,
|
||||
# action_generator=get_actions,
|
||||
# action_generator=get_all_actions,
|
||||
decision_maker=RandomDecisionMaker(seed=0),
|
||||
# decision_maker=InteractiveDecisionMaker(),
|
||||
termination_condition=termination_condition,
|
||||
check_conformance=True,
|
||||
check_conformance=False,
|
||||
verbose=True,
|
||||
renderer=render_woods,
|
||||
)
|
||||
|
||||
od = ODAPI(state, woods_rt_m, woods_rt_mm)
|
||||
|
||||
print()
|
||||
sim.run(od)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue