<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>Solving the TTC Model Execution Case with FunnyQT</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Tassilo Horn</string-name>
          <email>horn@uni-koblenz.de</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>Institute for Software Technology, University Koblenz-Landau</institution>
          ,
          <country country="DE">Germany</country>
        </aff>
      </contrib-group>
      <pub-date>
        <year>2015</year>
      </pub-date>
      <abstract>
        <p>This paper describes the FunnyQT solution to the TTC 2015 Model Execution transformation case. The solution solves the third variant of the case, i.e., it considers and implements the execution semantics of the complete UML Activity Diagram language. The solution won the most correct solution award. This paper describes the FunnyQT1 [1, 2] solution of the TTC 2015 Model Execution Case [3]. It implements the third variant of the case description, i.e., it implements the execution semantics of the complete UML Activity Diagram language. The solution project is available on Github2, and it is set up for easy reproduction on a SHARE image3. The solution has won the most correct solution award. FunnyQT is a model querying and transformation library for the functional Lisp dialect Clojure4. Queries and transformations are Clojure programs using the features provided by the FunnyQT API. Clojure provides strong metaprogramming capabilities that are used by FunnyQT in order to define several embedded domain-specific languages (DSL) for different querying and transformation tasks. FunnyQT is designed with extensibility in mind. By default, it supports EMF models and JGraLab TGraph models. Support for other modeling frameworks can be added without having to touch FunnyQT's internals. The FunnyQT API is structured into several namespaces, each namespace providing constructs supporting concrete querying and transformation use-cases, e.g., model management, functional querying, polymorphic functions, relational querying, pattern matching, in-place transformations, out-place transformations, bidirectional transformations, and some more. For solving the model execution case, only the model management, the functional querying, and the polymorphic functions APIs have been used.</p>
      </abstract>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>1 Introduction</title>
      <p>The explanations in the case description about the operational semantics on UML Activity Diagrams
suggest an algorithmic solution to the transformation case. The FunnyQT solution tries to be almost a
literal translation of the case description to Clojure code.</p>
      <p>FunnyQT is able to generate metamodel-specific model management APIs. This feature has been
used here. The generated API consists of element creation functions, lazy element sequence functions,
attribute access functions, and reference access functions. E.g., (a/create-ControlToken! ad) creates
a new control token and adds it to the activity diagram model ad, (a/isa-Token? x) returns true if and
only if x is a token, (a/all-Inputs ad) returns the lazy sequence of input elements in ad, (a/running?
n) and (a/set-running! n true) query and set the node n’s running attribute, and (a/-&gt;locals a),
(a/-&gt;set-locals! a ls), (a/-&gt;add-locals! a l), and (a/-&gt;remove-locals! a l) query, set, add
to, and remove from the locals reference of the activity a5.</p>
      <p>In the following, the solution is presented in a top-down manner similar to how the case
description defines the operational semantics of activity diagrams. The following listing shows the function
execute-activity-diagram which contains the transformation’s main loop.
1 (defn execute-activity-diagram [ad]
2 (let [activity (the (a/all-Activities ad))
3 trace (a/create-Trace! nil)]
4 (a/-&gt;set-trace! activity trace)
5 (init-variables activity (first (a/all-Inputs ad)))
6 (mapc #(a/set-running! % true) (a/-&gt;nodes activity))
7 (loop [en (first (filter a/isa-InitialNode? (a/-&gt;nodes activity)))]
8 (when en
9 (exec-node en)
10 (a/-&gt;add-executedNodes! trace en)
11 (recur (first (enabled-nodes activity)))))
12 trace))</p>
      <p>The function queries the single activity in the diagram, creates a new trace, and assigns that to the
activity. The activity’s variables are initialized and its nodes are set running.</p>
      <p>Then, a loop-recur iteration6 performs the actual execution of the activity. Initially, the variable en
is bound to the activity’s initial node, which gets executed and added to the trace. Thereafter, the loop
is restarted with the next enabled node. Eventually, there won’t be an enabled node left, and then the
function returns the trace.</p>
      <p>The first step in the execution of an activity is the initialization of its local and input variables. The
corresponding function init-variables is shown below. Local variables are set to their initial values,
and input variables are set to the input values.
13 (defn init-variables [activity input]
14 (doseq [lv (a/-&gt;locals activity)]
15 (when-let [init-value (a/-&gt;initialValue lv)]
16 (a/-&gt;set-currentValue! lv init-value)))
17 (doseq [iv (and input (a/-&gt;inputValues input))]
18 (when-let [val (a/-&gt;value iv)]
19 (a/-&gt;set-currentValue! (a/-&gt;variable iv) val))))</p>
      <p>After initializing the variables, the main function sets the activity’s nodes running, and the main loop
starts with the activity’s initial node.</p>
      <p>For different kinds of activity nodes, different execution semantics have to be encoded. This is exactly
the use-case of FunnyQT’s polymorphic functions (polyfn). A polymorphic function is declared once,
and then different implementations for instances of different metamodel types can be defined. When
the polyfn is called, a polymorphic dispatch based on the polyfn’s first argument’s metamodel type is
performed to pick out the right implementation7.</p>
      <p>The next listing shows the declaration of the polyfn exec-node and its implementation for initial
nodes. The declaration only defines the name of the polyfn and the number of its arguments (just one,
5The a/ prefix denotes a namespace into which the API has been generated. The API accesses models only using EMF’s
generic interfaces, thus this feature does not depend on code generation on the EMF side.</p>
      <p>6loop is not a loop in the sense of Java’s for or while but a local tail-recursion. The loop declares variables with their initial
bindings, and in the loop’s body recur forms may recurse back to the beginning of the loop providing new bindings for the
loop’s variables.</p>
      <p>7Polyfns support multiple inheritance. In case of an ambiguity during dispatch, e.g., two or more inherited implementations
are applicable, an error is signaled.
here). The implementation for initial nodes simply offers one new control token to the initial node’s
outgoing control flow edge.8
20 (declare-polyfn exec-node [node])
21 (defn offer-one-ctrl-token [node]
22 (let [ctrl-t (a/create-ControlToken! nil)
23 out-cf (the (a/-&gt;outgoing node))
24 offer (a/create-Offer! nil {:offeredTokens [ctrl-t]})]
25 (a/-&gt;add-heldTokens! node ctrl-t)
26 (a/-&gt;add-offers! out-cf offer)))
27 (defpolyfn exec-node InitialNode [i]
28 (offer-one-ctrl-token i))</p>
      <p>control flow whose guard variable’s current value is true9.
29 (defn pass-tokens
30 ([n] (pass-tokens n nil))
31 ([n out-cf]
32 (let [in-toks (consume-offers n)]
33 (a/-&gt;set-heldTokens! n in-toks)
34 (doseq [out-cf (if out-cf [out-cf] (a/-&gt;outgoing n))]
35 (a/-&gt;add-offers!
36 out-cf (a/create-Offer!
37 nil {:offeredTokens in-toks}))))))
38 (defpolyfn exec-node JoinNode [jn]
39 (pass-tokens jn))
40 (defpolyfn exec-node MergeNode [mn]
41 (pass-tokens mn))
42 (defpolyfn exec-node DecisionNode [dn]
43 (pass-tokens dn (the #(-&gt; % a/-&gt;guard a/-&gt;currentValue a/value)
44 (a/-&gt;outgoing dn))))</p>
      <p>The following listing shows the exec-node implementations for join, merge, and decision nodes.
Join and Merge nodes simply consume their input offers and pass the tokens they have been offered on
all outgoing control flows. Decision nodes act similar but offer their input tokens only on the outgoing</p>
      <p>How offers are consumed is defined by the consume-offers function shown below. The offers and
their tokens are calculated. Then, the offered tokens are divided into control and forked tokens. For
control tokens, their holder is unset. For forked tokens, the corresponding base token’s holder is unset.
The forked tokens’ remainingOffersCount is decremented. If it has become zero, the forked token is
removed from its holder. Lastly, the offers are deleted, and the incoming tokens are returned.
45 (defn consume-offers [node]
46 (let [offers (mapcat a/-&gt;offers (a/-&gt;incoming node))
47 tokens (mapcat a/-&gt;offeredTokens offers)
48 ctrl-toks (filter a/isa-ControlToken? tokens)
49 fork-toks (filter a/isa-ForkedToken? tokens)]
50 (doseq [ct ctrl-toks]
51 (a/-&gt;set-holder! ct nil))
52 (doseq [ft fork-toks]
53 (when-let [bt (a/-&gt;baseToken ft)]
54 (a/-&gt;set-holder! bt nil))
55 (a/set-remainingOffersCount! ft (dec (a/remainingOffersCount ft)))
56 (when (zero? (a/remainingOffersCount ft))
57 (a/-&gt;set-holder! ft nil)))
58 (mapc edelete! offers)
59 tokens))</p>
      <p>8The FunnyQT function the is similar to Clojure’s first except that it signals an error if the given collection contains zero
or more than one element. Thus, it makes the assumption that there must be only one outgoing control flow explicit.</p>
      <p>9(the predicate collection) returns the single element of the collection for which the predicate returns true. If there is
no or more elements satisfying the predicate, an error is signaled.</p>
      <p>The remaining kinds of activity nodes are fork nodes, activity final nodes and opaque actions. Their
exec-node implementations are printed in the next listing.</p>
      <p>A fork node consumes its offers and creates one forked token per incoming token. The incoming
tokens are set as the forked tokens’ base tokens, and the remaining offers count is set to the number of
outgoing control flows. All created forked tokens are offered on each outgoing control flow.
60 (defpolyfn exec-node ForkNode [fn]
61 (let [in-toks (consume-offers fn)
62 out-cfs (a/-&gt;outgoing fn)
63 out-toks (mapv #(a/create-ForkedToken!
64 nil {:baseToken %, :holder fn,
65 :remainingOffersCount (count out-cfs)})
66 in-toks)]
67 (a/-&gt;set-heldTokens! fn in-toks)
68 (doseq [out-cf out-cfs]
69 (a/-&gt;add-offers! out-cf (a/create-Offer!
70 nil {:offeredTokens out-toks})))))
71 (defpolyfn exec-node ActivityFinalNode [afn]
72 (consume-offers afn)
73 (mapc #(a/set-running! % false)
74 (-&gt; afn a/-&gt;activity a/-&gt;nodes)))
75 (defpolyfn exec-node OpaqueAction [oa]
76 (consume-offers oa)
77 (mapc eval-exp (a/-&gt;expressions oa))
78 (offer-one-ctrl-token oa))</p>
      <p>An activity final node simply consumes all offers and then sets the running attribute of all nodes
contained by the executed activity to false. An opaque action also consumes all offers, then evaluates all
its expressions in sequence using the eval-exp function, and finally offers one single control token on
the outgoing control flow.</p>
      <p>
        How an expression is evaluated depends on (
        <xref ref-type="bibr" rid="ref1">1</xref>
        ) its type and (
        <xref ref-type="bibr" rid="ref2">2</xref>
        ) on the value of its operator attribute.
The expression’s type is only important in order to separate unary from binary expressions, and the
operator defines the semantics. Therefore, the eval-exp function shown in the next listing has a special
case for boolean unary expressions which negates the expression’s current value using not. For all
binary expressions, the map op2fn mapping from operator enum constants to Clojure functions having
the semantics of that operator is used. The function determined by looking up the expression’s operator
is applied to both operands to compute the new value.
79 (def op2fn {(a/enum-IntegerCalculationOperator-ADD) +
80 (a/enum-IntegerCalculationOperator-SUBRACT)
81 (a/enum-IntegerComparisonOperator-SMALLER) &lt;
82 (a/enum-IntegerComparisonOperator-SMALLER_EQUALS) &lt;=
83 (a/enum-IntegerComparisonOperator-EQUALS) =
84 (a/enum-IntegerComparisonOperator-GREATER_EQUALS) &gt;=
85 (a/enum-IntegerComparisonOperator-GREATER) &gt;
86 (a/enum-BooleanBinaryOperator-AND) #(and %1 %2)
87 (a/enum-BooleanBinaryOperator-OR) #(or %1 %2)})
88 (defn eval-exp [exp]
89 (a/set-value! (-&gt; exp a/-&gt;assignee a/-&gt;currentValue)
90 (if (a/isa-BooleanUnaryExpression? exp)
91 (not (-&gt; exp a/-&gt;operand a/-&gt;currentValue a/value))
92 ((op2fn (a/operator exp))
93 (-&gt; exp a/-&gt;operand1 a/-&gt;currentValue a/value)
94 (-&gt; exp a/-&gt;operand2 a/-&gt;currentValue a/value)))))
      </p>
      <p>After executing all enabled nodes, the transformation’s main function execute-activity-diagram
recomputes the enabled nodes and resumes the execution. The enabled nodes are computed by the
enabled-nodes function shown in the following listing. The enabled nodes are those nodes of a given
activity which are set running, are no initial nodes10, and receive an offer on each incoming control flow,
or, in the case of a merge node, on one incoming control flow.</p>
      <p>These 101 NCLOC of algorithmic FunnyQT/Clojure code implement the complete operational
semantics of UML Activity Diagrams (with the exception of data flows which has not been demanded by
the case description).
3</p>
    </sec>
    <sec id="sec-2">
      <title>Evaluation &amp; Conclusion</title>
      <p>The solution comes with a test suite, and during the official evaluation achieved a full correctness score
winning the most correct solution award.</p>
      <p>With 101 lines of non-commented source code, the FunnyQT solution is quite concise. Of course,
understandability is a very subjective measure measure. The solution should be evident for any Clojure
programmer but even without prior Clojure knowledge, the solution shouldn’t be hard to follow due to
the usage of the metamodel-specific API. Another strong point is that all steps in the execution of an
activity are encoded in one function each whose definition is almost a literal translation of the English
description to FunnyQT/Clojure code.</p>
      <p>The following table shows the performance in terms of execution times of the FunnyQT solution for
all provided test models. These times were measured on a normal 4-core laptop with 2.6 GHz and 2 GB
of RAM dedicated to the JVM.</p>
      <p>Model Time Model Time Model Time
test1 1.3 ms test5 0.5 ms performance-variant-2 1246.5 ms
test2 0.6 ms test6 (false) 3.7 ms performance-variant-3-1 1159.6 ms
test3 4.1 ms test6 (true) 5.4 ms performance-variant-3-2 72.7 ms
test4 3.2 ms performance-variant-1 1104.0 ms</p>
      <p>When compared with the reference Java solution, the FunnyQT solution is slightly faster for all
normal and performance test models, and about 8 times faster for the performance-variant-3-1 model.</p>
      <p>Overall, FunnyQT seems to be very adequate for defining model interpreters. Especially its
polymorphic function facility has been explicitly designed for these kinds of tasks.
10Initial nodes have to be excluded because if they are set running, all of their (zero) incoming control flows have offers.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [1]
          <string-name>
            <given-names>Tassilo</given-names>
            <surname>Horn</surname>
          </string-name>
          (
          <year>2013</year>
          )
          <article-title>: Model Querying with FunnyQT - (Extended Abstract)</article-title>
          .
          <source>In Keith Duddy &amp; Gerti Kappel</source>
          , editors:
          <source>ICMT, Lecture Notes in Computer Science 7909</source>
          , Springer, pp.
          <fpage>56</fpage>
          -
          <lpage>57</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [2]
          <string-name>
            <given-names>Tassilo</given-names>
            <surname>Horn</surname>
          </string-name>
          (
          <year>2015</year>
          )
          <article-title>: Graph Pattern Matching as an Embedded Clojure DSL</article-title>
          . In: International Conference on Graph Transformation - 8th
          <source>International Conference, ICGT</source>
          <year>2015</year>
          , L'Aquila, Italy,
          <year>July 2015</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          [3]
          <string-name>
            <given-names>Tanja</given-names>
            <surname>Mayerhofer</surname>
          </string-name>
          &amp; Manuel
          <string-name>
            <surname>Wimmer</surname>
          </string-name>
          (
          <year>2015</year>
          ):
          <article-title>The TTC 2015 Model Execution Case</article-title>
          .
          <source>In: Transformation Tool Contest</source>
          <year>2015</year>
          .
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>