浏览代码

Implemented additional "arena" hierarchical priority semantics + corrected existing semantics (Source-Child semantics does no longer reverse "explicit" (document) order of transitions of equal priority). Add tests for hierarchical priority semantics.

Joeri Exelmans 5 年之前
父节点
当前提交
d6c05d3510

+ 41 - 14
src/sccd/statechart/dynamic/round.py

@@ -20,7 +20,12 @@ if timer.TIMINGS:
         print("\ncache hits: %s, cache misses: %s" %(ctr.cache_hits, ctr.cache_misses))
     atexit.register(print_stats)
 
+
 class GeneratorStrategy(ABC):
+    __slots__ = ["priority_ordered_transitions"]
+    def __init__(self, priority_ordered_transitions):
+        self.priority_ordered_transitions = priority_ordered_transitions
+
     @abstractmethod
     def cache_init(self):
         pass
@@ -30,7 +35,7 @@ class GeneratorStrategy(ABC):
         pass
 
     @abstractmethod
-    def generate(self, execution, enabled_events, events_bitmap, forbidden_arenas):
+    def generate(self, execution, events_bitmap, forbidden_arenas):
         pass
 
     @abstractmethod
@@ -38,10 +43,8 @@ class GeneratorStrategy(ABC):
         pass
 
 
+# First filter on current state, then filter on current events
 class CurrentConfigStrategy(GeneratorStrategy):
-    __slots__ = ["statechart"]
-    def __init__(self, statechart):
-        self.statechart = statechart
 
     def cache_init(self):
         return {}
@@ -49,36 +52,60 @@ class CurrentConfigStrategy(GeneratorStrategy):
     def key(self, execution, events_bitmap, forbidden_arenas):
         return (execution.configuration, forbidden_arenas)
 
-    def generate(self, execution, enabled_events, events_bitmap, forbidden_arenas):
-        return [ t for state_id in bm_items(execution.configuration)
-                      for t in self.statechart.tree.state_list[state_id].transitions
-                       if (not forbidden_arenas & t.opt.arena_bitmap) ]
+    def generate(self, execution, events_bitmap, forbidden_arenas):
+        return [ t for t in self.priority_ordered_transitions
+                      if (t.source.opt.state_id_bitmap & execution.configuration)
+                       and (not forbidden_arenas & t.opt.arena_bitmap) ]
 
     def filter_f(self, execution, enabled_events, events_bitmap):
         return lambda t: (not t.trigger or t.trigger.check(events_bitmap)) and execution.check_guard(t, enabled_events)
 
+# First filter based on current events, then filter on current state
 class EnabledEventsStrategy(GeneratorStrategy):
     __slots__ = ["statechart"]
-    def __init__(self, statechart):
+    def __init__(self, priority_ordered_transitions, statechart):
+        super().__init__(priority_ordered_transitions)
         self.statechart = statechart
 
     def cache_init(self):
         cache = {}
         for event_id in bm_items(self.statechart.events):
             events_bitmap = bit(event_id)
-            cache[(events_bitmap, 0)] = self.generate(None, None, events_bitmap, 0)
+            cache[(events_bitmap, 0)] = self.generate(None, events_bitmap, 0)
         return cache
 
     def key(self, execution, events_bitmap, forbidden_arenas):
         return (events_bitmap, forbidden_arenas)
 
-    def generate(self, execution, enabled_events, events_bitmap, forbidden_arenas):
-        return [ t for t in self.statechart.tree.transition_list
+    def generate(self, execution, events_bitmap, forbidden_arenas):
+        return [ t for t in self.priority_ordered_transitions
+                      if (not t.trigger or t.trigger.check(events_bitmap))
+                      and (not forbidden_arenas & t.opt.arena_bitmap) ]
+
+    def filter_f(self, execution, enabled_events, events_bitmap):
+        return lambda t: (execution.configuration & t.source.opt.state_id_bitmap) and execution.check_guard(t, enabled_events)
+
+class CurrentConfigAndEnabledEventsStrategy(GeneratorStrategy):
+    __slots__ = ["statechart"]
+    def __init__(self, priority_ordered_transitions, statechart):
+        super().__init__(priority_ordered_transitions)
+        self.statechart = statechart
+
+    def cache_init(self):
+        return {}
+
+    def key(self, execution, events_bitmap, forbidden_arenas):
+        return (execution.configuration, events_bitmap, forbidden_arenas)
+
+    def generate(self, execution, events_bitmap, forbidden_arenas):
+        return [ t for t in self.priority_ordered_transitions
                       if (not t.trigger or t.trigger.check(events_bitmap))
+                      and (t.source.opt.state_id_bitmap & execution.configuration)
                       and (not forbidden_arenas & t.opt.arena_bitmap) ]
 
     def filter_f(self, execution, enabled_events, events_bitmap):
-        return lambda t: execution.check_source(t) and execution.check_guard(t, enabled_events)
+        return lambda t: execution.check_guard(t, enabled_events)
+
 
 class CandidateGenerator:
     __slots__ = ["strategy", "cache"]
@@ -94,7 +121,7 @@ class CandidateGenerator:
             candidates = self.cache[key]
             ctr.cache_hits += 1
         except KeyError:
-            candidates = self.cache[key] = self.strategy.generate(execution, enabled_events, events_bitmap, forbidden_arenas)
+            candidates = self.cache[key] = self.strategy.generate(execution, events_bitmap, forbidden_arenas)
             ctr.cache_misses += 1
 
         return filter(self.strategy.filter_f(execution, enabled_events, events_bitmap), candidates)

+ 0 - 3
src/sccd/statechart/dynamic/statechart_execution.py

@@ -121,9 +121,6 @@ class StatechartExecution:
             e.args = ("While checking guard of transition %s:\n" % str(t) +str(e),)
             raise
 
-    def check_source(self, t) -> bool:
-        return self.configuration & t.source.opt.state_id_bitmap
-
     @staticmethod
     def _perform_actions(ctx: EvalContext, actions: List[Action]):
         for a in actions:

+ 13 - 6
src/sccd/statechart/dynamic/statechart_instance.py

@@ -35,13 +35,20 @@ class StatechartInstance(Instance):
         if semantics.has_multiple_variants():
             raise Exception("Cannot execute model with multiple semantics.")
 
-        reverse = semantics.priority == Priority.SOURCE_CHILD
+        priority_function = {
+            Priority.SOURCE_PARENT: priority_source_parent,
+            Priority.SOURCE_CHILD: priority_source_child,
+            Priority.ARENA_PARENT: priority_arena_parent,
+            Priority.ARENA_CHILD: priority_arena_child,
+        }[semantics.priority]
 
-        # 2 transition candidate generation algorithms to choose from!
-        # generator = CandidatesGeneratorCurrentConfigBased(statechart, reverse)
-        # generator = CandidatesGeneratorEventBased(statechart, reverse)
-        # generator = CandidateGenerator(CurrentConfigStrategy(statechart))
-        generator = CandidateGenerator(EnabledEventsStrategy(statechart))
+        priority_ordered_transitions = priority_function(statechart.tree)
+
+        # strategy = CurrentConfigStrategy(priority_ordered_transitions)
+        strategy = EnabledEventsStrategy(priority_ordered_transitions, statechart)
+        # strategy = CurrentConfigAndEnabledEventsStrategy(priority_ordered_transitions, statechart)
+
+        generator = CandidateGenerator(strategy)
 
         # Big step + combo step maximality semantics
 

+ 60 - 3
src/sccd/statechart/static/tree.py

@@ -1,5 +1,6 @@
 import termcolor
 from typing import *
+import itertools
 from sccd.statechart.static.action import *
 from sccd.util.bitmap import *
 from sccd.util import timer
@@ -27,7 +28,6 @@ class State(Freezable):
 
         self.opt: Optional['StateOptimization'] = None
 
-    # def __post_init__(self):
         if self.parent is not None:
             self.parent.children.append(self)
 
@@ -189,12 +189,13 @@ class Transition(Freezable):
 
 # Data that is generated for each state.
 class StateOptimization(Freezable):
-    __slots__ = ["full_name", "state_id", "state_id_bitmap", "ancestors", "descendants", "history", "ts_static", "ts_dynamic", "after_triggers"]
+    __slots__ = ["full_name", "depth", "state_id", "state_id_bitmap", "ancestors", "descendants", "history", "ts_static", "ts_dynamic", "after_triggers"]
     def __init__(self):
         super().__init__()
 
         self.full_name: str = ""
 
+        self.depth: int -1 # Root is 0, root's children are 1, and so on
         self.state_id: int = -1
         self.state_id_bitmap: Bitmap = Bitmap() # bitmap with only state_id-bit set
 
@@ -273,6 +274,10 @@ def optimize_tree(root: State) -> StateTree:
 
             return f
 
+        def assign_depth(state: State, parent_depth: int = 0):
+            state.opt.depth = parent_depth + 1
+            return parent_depth + 1
+
         def assign_full_name(state: State, parent_full_name: str = ""):
             if state is root:
                 full_name = '/'
@@ -330,6 +335,7 @@ def optimize_tree(root: State) -> StateTree:
             before_children=[
                 init_opt(),
                 assign_full_name,
+                assign_depth,
                 add_to_list,
                 set_ancestors,
             ],
@@ -342,7 +348,7 @@ def optimize_tree(root: State) -> StateTree:
 
 
         for t in transition_list:
-            # Arena can be computed statically. First computer Lowest-common ancestor:
+            # Arena can be computed statically. First compute Lowest-common ancestor:
             # Intersection between source & target ancestors, last member in depth-first sorted state list.
             lca_id = bm_highest_bit(t.source.opt.ancestors & t.targets[0].opt.ancestors)
             lca = state_list[lca_id]
@@ -389,3 +395,54 @@ def optimize_tree(root: State) -> StateTree:
             t.freeze()
 
         return StateTree(root, transition_list, state_list, state_dict, after_triggers, stable_bitmap, history_states)
+
+
+def priority_source_parent(tree: StateTree) -> List[Transition]:
+    # Tree's transition list already ordered parent-first
+    return tree.transition_list
+
+def priority_source_child(tree: StateTree) -> List[Transition]:
+    # Build a child-first order of transitions, while maintaining document order between transitions at the same level
+    transition_list = []
+    def append(state: State, _=None):
+        transition_list.extend(state.transitions)
+    visit_tree(tree.root, lambda s: s.children, before_children=[], after_children=[append])
+    return transition_list
+
+def priority_arena_parent(tree: StateTree) -> List[Transition]:
+    # partial ordering key + Python's stable sorting = desired outcome
+    return list(sorted(tree.transition_list, key=lambda t: t.opt.arena.opt.depth))
+
+def priority_arena_child(tree: StateTree) -> List[Transition]:
+    return list(sorted(tree.transition_list, key=lambda t: -t.opt.arena.opt.depth))
+
+
+def concurrency_arena_orthogonal(tree: StateTree):
+    with timer.Context("concurrency_arena_orthogonal"):
+        import collections
+        nonoverlapping = collections.defaultdict(list)
+        for t1,t2 in itertools.combinations(tree.transition_list, r=2):
+            if not (t1.opt.arena_bitmap & t2.opt.arena_bitmap):
+                nonoverlapping[t1].append(t2)
+                nonoverlapping[t2].append(t1)
+
+        for t, ts in nonoverlapping.items():
+            print(str(t), "does not overlap with", ",".join(str(t) for t in ts))
+
+        print(len(nonoverlapping), "nonoverlapping pairs of transitions")
+
+def concurrency_src_dst_orthogonal(tree: StateTree):
+    with timer.Context("concurrency_src_dst_orthogonal"):
+        import collections
+        nonoverlapping = collections.defaultdict(list)
+        for t1,t2 in itertools.combinations(tree.transition_list, r=2):
+            lca_src = tree.state_list[bm_highest_bit(t1.source.opt.ancestors & t2.source.opt.ancestors)]
+            lca_dst = tree.state_list[bm_highest_bit(t1.targets[0].opt.ancestors & t2.targets[0].opt.ancestors)]
+            if isinstance(lca_src, ParallelState) and isinstance(lca_dst, ParallelState):
+                nonoverlapping[t1].append(t2)
+                nonoverlapping[t2].append(t1)
+
+        for t, ts in nonoverlapping.items():
+            print(str(t), "does not overlap with", ",".join(str(t) for t in ts))
+
+        print(len(nonoverlapping), "nonoverlapping pairs of transitions")

+ 135 - 0
test/test_files/semantics/priority/statechart_priority_hierarchical.svg

@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: state transitions Pages: 1 -->
+<svg width="346pt" height="578pt"
+ viewBox="0.00 0.00 346.00 577.50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 573.5)">
+<title>state transitions</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-573.5 342,-573.5 342,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster__s6</title>
+<path fill="none" stroke="#000000" stroke-width="2" d="M270,-148C270,-148 318,-148 318,-148 324,-148 330,-154 330,-160 330,-160 330,-310.5 330,-310.5 330,-316.5 324,-322.5 318,-322.5 318,-322.5 270,-322.5 270,-322.5 264,-322.5 258,-316.5 258,-310.5 258,-310.5 258,-160 258,-160 258,-154 264,-148 270,-148"/>
+<text text-anchor="start" x="287.6646" y="-303.7" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s6</text>
+</g>
+<g id="clust2" class="cluster">
+<title>cluster__s1</title>
+<path fill="none" stroke="#000000" stroke-width="2" d="M20,-8C20,-8 201,-8 201,-8 207,-8 213,-14 213,-20 213,-20 213,-518.5 213,-518.5 213,-524.5 207,-530.5 201,-530.5 201,-530.5 20,-530.5 20,-530.5 14,-530.5 8,-524.5 8,-518.5 8,-518.5 8,-20 8,-20 8,-14 14,-8 20,-8"/>
+<text text-anchor="start" x="104.1646" y="-511.7" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s1</text>
+</g>
+<g id="clust3" class="cluster">
+<title>cluster__s1_s2</title>
+<path fill="none" stroke="#000000" stroke-width="2" d="M98,-16C98,-16 193,-16 193,-16 199,-16 205,-22 205,-28 205,-28 205,-423.5 205,-423.5 205,-429.5 199,-435.5 193,-435.5 193,-435.5 98,-435.5 98,-435.5 92,-435.5 86,-429.5 86,-423.5 86,-423.5 86,-28 86,-28 86,-22 92,-16 98,-16"/>
+<text text-anchor="start" x="139.1646" y="-416.7" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s2</text>
+</g>
+<!-- __initial -->
+<g id="node1" class="node">
+<title>__initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="163" cy="-564" rx="5.5" ry="5.5"/>
+</g>
+<!-- _s1 -->
+<!-- __initial&#45;&gt;_s1 -->
+<g id="edge1" class="edge">
+<title>__initial&#45;&gt;_s1</title>
+<path fill="none" stroke="#000000" d="M163,-558.4623C163,-554.2143 163,-547.8733 163,-540.6925"/>
+<polygon fill="#000000" stroke="#000000" points="166.5001,-540.4976 163,-530.4976 159.5001,-540.4976 166.5001,-540.4976"/>
+<text text-anchor="middle" x="164.3895" y="-541.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _s6 -->
+<!-- _s6_initial -->
+<g id="node3" class="node">
+<title>_s6_initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="294" cy="-279" rx="5.5" ry="5.5"/>
+</g>
+<!-- _s6_s7 -->
+<g id="node4" class="node">
+<title>_s6_s7</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="322,-192 266,-192 266,-156 322,-156 322,-192"/>
+<text text-anchor="start" x="287.6646" y="-170.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s7</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M278.3333,-157C278.3333,-157 309.6667,-157 309.6667,-157 315.3333,-157 321,-162.6667 321,-168.3333 321,-168.3333 321,-179.6667 321,-179.6667 321,-185.3333 315.3333,-191 309.6667,-191 309.6667,-191 278.3333,-191 278.3333,-191 272.6667,-191 267,-185.3333 267,-179.6667 267,-179.6667 267,-168.3333 267,-168.3333 267,-162.6667 272.6667,-157 278.3333,-157"/>
+</g>
+<!-- _s6_initial&#45;&gt;_s6_s7 -->
+<g id="edge2" class="edge">
+<title>_s6_initial&#45;&gt;_s6_s7</title>
+<path fill="none" stroke="#000000" d="M294,-273.3547C294,-260.2193 294,-226.8662 294,-202.4137"/>
+<polygon fill="#000000" stroke="#000000" points="297.5001,-202.2371 294,-192.2371 290.5001,-202.2372 297.5001,-202.2371"/>
+<text text-anchor="middle" x="295.3895" y="-235" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _s1_initial -->
+<g id="node6" class="node">
+<title>_s1_initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="94" cy="-487" rx="5.5" ry="5.5"/>
+</g>
+<!-- _s1_s2 -->
+<!-- _s1_initial&#45;&gt;_s1_s2 -->
+<g id="edge3" class="edge">
+<title>_s1_initial&#45;&gt;_s1_s2</title>
+<path fill="none" stroke="#000000" d="M94,-481.3418C94,-473.9327 94,-460.1666 94,-445.6217"/>
+<polygon fill="#000000" stroke="#000000" points="97.5001,-445.4962 94,-435.4963 90.5001,-445.4963 97.5001,-445.4962"/>
+<text text-anchor="middle" x="95.3895" y="-455.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _s1_s5 -->
+<g id="node7" class="node">
+<title>_s1_s5</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="72,-131 16,-131 16,-95 72,-95 72,-131"/>
+<text text-anchor="start" x="37.6646" y="-109.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s5</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M28.3333,-96C28.3333,-96 59.6667,-96 59.6667,-96 65.3333,-96 71,-101.6667 71,-107.3333 71,-107.3333 71,-118.6667 71,-118.6667 71,-124.3333 65.3333,-130 59.6667,-130 59.6667,-130 28.3333,-130 28.3333,-130 22.6667,-130 17,-124.3333 17,-118.6667 17,-118.6667 17,-107.3333 17,-107.3333 17,-101.6667 22.6667,-96 28.3333,-96"/>
+</g>
+<!-- _s1_s2&#45;&gt;_s1_s5 -->
+<g id="edge8" class="edge">
+<title>_s1_s2&#45;&gt;_s1_s5</title>
+<path fill="none" stroke="#000000" d="M86.0033,-390.4557C70.5295,-387.2377 38,-379.2534 38,-369 38,-369 38,-369 38,-331 38,-262.8788 40.9637,-182.8291 42.7253,-141.2669"/>
+<polygon fill="#000000" stroke="#000000" points="46.2303,-141.2247 43.1657,-131.0828 39.2368,-140.9222 46.2303,-141.2247"/>
+<text text-anchor="start" x="39" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">^out.source_parent &#160;&#160;</text>
+</g>
+<!-- _s1_s2_initial -->
+<g id="node9" class="node">
+<title>_s1_s2_initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="169" cy="-392" rx="5.5" ry="5.5"/>
+</g>
+<!-- _s1_s2_s3 -->
+<g id="node11" class="node">
+<title>_s1_s2_s3</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="197,-256 141,-256 141,-220 197,-220 197,-256"/>
+<text text-anchor="start" x="162.6646" y="-234.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s3</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M153.3333,-221C153.3333,-221 184.6667,-221 184.6667,-221 190.3333,-221 196,-226.6667 196,-232.3333 196,-232.3333 196,-243.6667 196,-243.6667 196,-249.3333 190.3333,-255 184.6667,-255 184.6667,-255 153.3333,-255 153.3333,-255 147.6667,-255 142,-249.3333 142,-243.6667 142,-243.6667 142,-232.3333 142,-232.3333 142,-226.6667 147.6667,-221 153.3333,-221"/>
+</g>
+<!-- _s1_s2_initial&#45;&gt;_s1_s2_s3 -->
+<g id="edge4" class="edge">
+<title>_s1_s2_initial&#45;&gt;_s1_s2_s3</title>
+<path fill="none" stroke="#000000" d="M169,-386.3288C169,-381.6736 169,-374.9097 169,-369 169,-369 169,-369 169,-279 169,-274.9516 169,-270.6784 169,-266.48"/>
+<polygon fill="#000000" stroke="#000000" points="172.5001,-266.3687 169,-256.3688 165.5001,-266.3688 172.5001,-266.3687"/>
+<text text-anchor="middle" x="170.3895" y="-342.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _s1_s2_s4 -->
+<g id="node10" class="node">
+<title>_s1_s2_s4</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="150,-60 94,-60 94,-24 150,-24 150,-60"/>
+<text text-anchor="start" x="115.6646" y="-38.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s4</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M106.3333,-25C106.3333,-25 137.6667,-25 137.6667,-25 143.3333,-25 149,-30.6667 149,-36.3333 149,-36.3333 149,-47.6667 149,-47.6667 149,-53.3333 143.3333,-59 137.6667,-59 137.6667,-59 106.3333,-59 106.3333,-59 100.6667,-59 95,-53.3333 95,-47.6667 95,-47.6667 95,-36.3333 95,-36.3333 95,-30.6667 100.6667,-25 106.3333,-25"/>
+</g>
+<!-- _s1_s2_s3&#45;&gt;_s6_s7 -->
+<g id="edge7" class="edge">
+<title>_s1_s2_s3&#45;&gt;_s6_s7</title>
+<path fill="none" stroke="#000000" d="M179.54,-219.997C184.5548,-212.8355 191.1433,-205.102 198.83,-200 215.729,-188.7834 237.3577,-182.3804 255.7245,-178.7379"/>
+<polygon fill="#000000" stroke="#000000" points="256.5888,-182.1392 265.8208,-176.9409 255.3622,-175.2475 256.5888,-182.1392"/>
+<text text-anchor="start" x="198" y="-203" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">^out.arena_parent &#160;&#160;</text>
+</g>
+<!-- _s1_s2_s3&#45;&gt;_s1_s5 -->
+<g id="edge5" class="edge">
+<title>_s1_s2_s3&#45;&gt;_s1_s5</title>
+<path fill="none" stroke="#000000" d="M140.7834,-222.0043C139.5082,-221.3206 138.2428,-220.6496 137,-220 111.6952,-206.7722 99.0415,-212.5113 79.177,-192 65.4242,-177.7994 56.5127,-157.3074 51.1006,-140.7526"/>
+<polygon fill="#000000" stroke="#000000" points="54.411,-139.6075 48.171,-131.0452 47.7095,-141.63 54.411,-139.6075"/>
+<text text-anchor="start" x="80" y="-171" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">^out.source_child &#160;&#160;</text>
+</g>
+<!-- _s1_s2_s3&#45;&gt;_s1_s2_s4 -->
+<g id="edge6" class="edge">
+<title>_s1_s2_s3&#45;&gt;_s1_s2_s4</title>
+<path fill="none" stroke="#000000" d="M170.2855,-219.8368C171.1448,-201.4912 171.2972,-172.4466 166,-148 159.9258,-119.9676 146.77,-89.9441 136.4175,-69.0327"/>
+<polygon fill="#000000" stroke="#000000" points="139.5361,-67.4437 131.8938,-60.1058 133.2921,-70.608 139.5361,-67.4437"/>
+<text text-anchor="start" x="161" y="-110" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">^out.arena_child &#160;&#160;</text>
+</g>
+</g>
+</svg>

+ 42 - 0
test/test_files/semantics/priority/statechart_priority_hierarchical.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0" ?>
+<statechart>
+
+  <inport name="in">
+    <event name="start"/>
+  </inport>
+
+  <outport name="out">
+    <event name="source_parent"/>
+    <event name="source_child"/>
+    <event name="arena_parent"/>
+    <event name="arena_child"/>
+  </outport>
+
+  <root initial="s1">
+    <state id="s1" initial="s2">
+      <state id="s2" initial="s3">
+        <state id="s3">
+          <transition target="/s1/s5">
+            <!-- with source-child, this transition is preferred above the other
+                 outgoing ones, because this one occurs first in the document -->
+            <raise event="source_child"/>
+          </transition>
+          <transition target="../s4">
+            <raise event="arena_child"/>
+          </transition>
+          <transition target="/s6/s7">
+            <raise event="arena_parent"/>
+          </transition>
+        </state>
+        <state id="s4"/>
+        <transition target="../s5">
+          <raise event="source_parent"/>
+        </transition>
+      </state>
+      <state id="s5"/>
+    </state>
+    <state id="s6">
+      <state id="s7"/>
+    </state>
+  </root>
+</statechart>

+ 16 - 0
test/test_files/semantics/priority/test_arena_child.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<test>
+  <statechart src="statechart_priority_hierarchical.xml">
+    <override_semantics
+      big_step_maximality="take_one"
+      priority="arena_child"/>
+  </statechart>
+  <input>
+      <event name="start" port="in" time="0 d"/>
+  </input>
+  <output>
+    <big_step>
+      <event name="arena_child" port="out"/>
+    </big_step>
+  </output>
+</test>

+ 16 - 0
test/test_files/semantics/priority/test_arena_parent.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<test>
+  <statechart src="statechart_priority_hierarchical.xml">
+    <override_semantics
+      big_step_maximality="take_one"
+      priority="arena_parent"/>
+  </statechart>
+  <input>
+      <event name="start" port="in" time="0 d"/>
+  </input>
+  <output>
+    <big_step>
+      <event name="arena_parent" port="out"/>
+    </big_step>
+  </output>
+</test>

+ 16 - 0
test/test_files/semantics/priority/test_source_child.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<test>
+  <statechart src="statechart_priority_hierarchical.xml">
+    <override_semantics
+      big_step_maximality="take_one"
+      priority="source_child"/>
+  </statechart>
+  <input>
+      <event name="start" port="in" time="0 d"/>
+  </input>
+  <output>
+    <big_step>
+      <event name="source_child" port="out"/>
+    </big_step>
+  </output>
+</test>

+ 16 - 0
test/test_files/semantics/priority/test_source_parent.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<test>
+  <statechart src="statechart_priority_hierarchical.xml">
+    <override_semantics
+      big_step_maximality="take_one"
+      priority="source_parent"/>
+  </statechart>
+  <input>
+      <event name="start" port="in" time="0 d"/>
+  </input>
+  <output>
+    <big_step>
+      <event name="source_parent" port="out"/>
+    </big_step>
+  </output>
+</test>