=Paper=
{{Paper
|id=Vol-2310/paper9
|storemode=property
|title=YAMTL Solution to the TTC 2018 Social Media Case
|pdfUrl=https://ceur-ws.org/Vol-2310/paper9.pdf
|volume=Vol-2310
|authors=Artur Boronat
|dblpUrl=https://dblp.org/rec/conf/staf/Boronat18
}}
==YAMTL Solution to the TTC 2018 Social Media Case==
YAMTL Solution to the TTC 2018 Social Media Case Artur Boronat Department of Informatics, University of Leicester, UK aboronat@le.ac.uk Abstract Software models raise the level of abstraction of software artefacts in- volved in the design, implementation and testing phases of software systems. Such models may be used to automate many of the tasks in- volved in them, where queries play an important role. Moreover, some of those models may be inferred automatically from existing software artefacts, e.g., by means of reverse engineering, yielding potentially very large models (VLMs). Technology to analyse VLMs efficiently enables the application of model-driven software development in industry and is the subject of study in the TTC 2018 Social Media Case. YAMTL is both a model transformation (MT) language that is available as an internal DSL of Xtend and a companion MT engine that can be used from any JVM application and that supports incremental execution of MT. In this paper, we present the YAMTL solution to the social media case and discuss its performance, scalability and memory usage w.r.t. the reference solution. The YAMTL solution was deemed to be the most scalable solution at the TTC 2018. 1 Introduction The goal of the TTC 2018 Social Media Case [Hin18] aims at performing two analyses of a social media network, represented as an instance of a static object model of a social network, in order to find the three most influential posts and comments according to certain criteria. Both analyses should be implemented as model queries and should be applied over a specific network. The networks to be analysed are subject to updates and an important challenge of the case is to refresh the conclusions from each analysis in the most efficient manner after each network update. In this paper, we present a solution to the case with YAMTL [Bor18], a MT language available from within Xtend [Fou18] and its companion MT engine that can execute such model transformations in an incremental way. Therefore, the analyses to be performed are implemented using model transformations and YAMTL’s machinery for tracking model dependencies, for detecting the impact of an udpate and for re-executing only the affected part of the model transformation are reused in order to implement model queries. The relevant excerpts of YAMTL for defining MT are explained in section 2 and the YAMTL solution is discussed in section 3. Finally, the solution is discussed in section 4 using the performance results obtained with the case benchmark framework along its evaluation criteria [Hin18]. 2 YAMTL Outline YAMTL is a MT language provided as an internal DSL of Xtend [Fou18] that augments it with MT modules, which can be used to specify declarative MTs. In this subsection, the excerpt of YAMTL that is relevant to Copyright held by the author(s). In: A. Garcia-Dominguez, G. Hinkel and F. Krikava (eds.): Proceedings of the 11th Transformation Tool Contest, Toulouse, France, 29-06-2018, published at http://ceur-ws.org the solution is presented. In particular, MT modules used to implement queries consist of helpers and of rules, whose computation logic is performed in the source pattern, at matching time. Hence, the interpretation of a query is performed by the YAMTL pattern matcher and rules are scheduled but not fully executed. The declaration of a YAMTL transformation module starts by creating a specialization of the class YAMTLModule, as shown in Appendix A.2. Within its constructor, the header() of the transformation defines its signature, declaring its input and output models, and the ruleStore() contains the declaration of rules. Each rule has an input pattern for matching variables and an output pattern for creating objects. An input pattern consists of in elements together with a global rule filter condition, which is true if not specified. Each of the in elements is declared with a variable name, a type and a local filter condition, which is true if not specified. A rule is applied whenever a match for each in variable is found such that both the correspond- ing local filter conditions and the global filter condition are satisfied. In each filter condition, the expression ’variable’.fetch as Type fetches the value of ’variable’ from the execution environment. This is normally used to access matched objects in a filter expression. An output pattern consists of out elements, each of which is declared with a variable name, a type and an action block. Filter conditions and action blocks are specified as non-pure lambda expressions in Xtend. In the YAMTL solution presented in this paper, output pattern elements will be declared as no-op as we are only interested in the query side of a transformation rule. When loading a YAMTL transformation definition, YAMTL initializes transformation rules by inserting their abstract representation in the rule store. Once the rule store is initialized, rules are type checked. If no error arises, the pattern matcher finds all rule matches and schedules them. During this phase, the input pattern elements are ordered by the size of their type extent and YAMTL finds a match for a rule by first mapping matched in elements to objects that satisfy the corresponding filter condition. When a total match is found1 , the satisfaction of that match is finally asserted by the rule filter condition. A rule is scheduled, and traced, as a transformation step, which consists of a labelled pair of two matches, the match for the input pattern of the rule, which enables its application, and the match for the output pattern of the rule, which is only initialized when the transformation step is executed. This last step is skipped when scheduling transformation rules only, for executing queries, using the execution phase MATCH_ONLY. The MT engine has been extended with an incremental execution mode, which consists of two phases: the initial phase, the transformation is executed in batch mode but, additionally, tracks feature calls in objects of the source model involved in transformation steps as dependencies; and the propagation phase, the transformation is executed incrementally for a given source update and only those transformation steps affected by the update are (re-)executed. This means that components of YAMTL’s execution model have been extended but the syntax used to define model transformations is preserved. Hence, a YAMTL batch model transformation can be executed in incremental mode, without any additional user specification overhead. In YAMTL, model udpates are represented using the Eclipse Modeling Framework change model[SBPM09]. Given a model update δs between a source model Ms , already synchronized with a target model Mt via a model ∗ ∗ transformation t : Ms − → Mt (where − → denotes a sequence of transformation steps), and an updated source model Ms0 , YAMTL propagates the model update δs along t. The effect of this forward propagation is the application of an update δt on the target model Mt . 3 The YAMTL Solution The solutions to both task one and task two exploit a common functional requirement − both of them require the selection of the three best submissions and both queries only remember these three elements during query evaluation. For each computationally-relevant statement in a query that uses universal quantification we define a model transformation rule, where the input pattern consists of a single in element, in which the variable corresponds to the universal variable and the filter condition corresponds to the predicate preceded by the quantifier. The score used to rank each submission is computed in the filter. The YAMTL solutions employ two auxiliary data structures, one for storing the threeBestCandidates, when their score is greater than zero, and a list of candidatesWithNilScore. When a submission is processed, by evaluating the filter condition of the corresponding rule, if its score is zero, it is added to candidatesWithNilScore. Otherwise, if its score is greater than zero, it is added to threeBestCandidates using the method Util.addIfIsThreeBest(list, submission, score), shown in Appendix A.1, which inserts the candidate in the list if its score is greater than the score of one of the elements in the list. The insertion 1 YAMTL supports more complex source patterns with: several in elements, which may be functionally dependent on previously matched in elements; and derived in elements [Bor18]. process starts from the end of the list (i.e., from the current best candidate with the smallest score) and proceeds towards the head of the list if the score of the element to be inserted is greater than the current best candidate score. When the submission reaches the first position of the list, it is inserted. When the score of a current best candidate (in either the first or the second position) cannot be improved, the submission is inserted after it. When there is a tie, submission timestamps are used to resolve it (using the most recent submission as the better candidate). If the insertion causes the list to grow, the last element is trimmed so that only the three best candidates are kept at any one time. A query is then evaluated by executing the corresponding model transformation in MATCH_ONLY mode. The result is obtained with the expression (xform as TaskXModule).getBestThree().map[id].join(’|’), which completes the list of best candidates with submissions with nil score if its length is smaller than three by using the method Util.getBestThree(weightedList, nilScoreSubmissionList), shown in Appendix A.1. When the list of best candidates is shorter than three, this method sorts the list of submissions with nil score, using their timestamps, before selecting as many submissions as needed to complete the list of threeBestCandidates. This means that sorting submissions with nil score is skipped quite frequently. The integration of the solution into the benchmark framework is achieved by extending the harness class Solution with three methods that need to be overriden: the constructor, where the transforma- tion is configured; Initial(), which performs the initial transformation; and Update(delta), which re- computes the query for a given model udpate. In the batch variant of the solution, the initial step is achieved by invoking the method xform.execute() and the update step is achieved by resetting the in- ternal caches and the lists threeBestCandidates and candidatesWithNilScore, by applying the source update with xform.applyDelta(’sn’, deltaName), and by applying the transformation from scratch with xform.execute(). The benchmark harnesses for tasks one and two can be found in B.1.1 and B.1.2, respectively. In the incremental variant of the solution, the initial step is performed as in the batch variant but the update step is achieved by propagating the delta identified by deltaName using the expression xform.propagateDelta(’sn’, deltaName), which applies the delta to the source model and then re-executes the model query only for the parts of the model that have been modified. The benchmark harnesses for tasks one and two can be found in B.2.1 and B.2.2, respectively. In the following subsections, the discussion focusses on task-specific query details that are not common to both tasks. The complete code of the implementation of the queries in YAMTL can be found in Appendix A.2 for task one and in in Appendix A.3 for task two. It is important to note that the query solutions using YAMTL modules are the same for both variants (batch and incremental). 3.1 Task 1: Most Controversial Posts The goal in the first task is to obtain the most controversial posts, that is those which spark off more comments. The query is implemented as a transformation rule, whose input pattern is formed by an in element that will match posts that contain comments as indicated in the filter. In particular, the method post.getAllComments() fetches all contained Comments within the matched post. If the list of contained comments has elements, the score of the post is computed by adding, for each comment, the sum of 10 plus the amount of users that liked the com- ment. Then the post is recorded with the method Util.addIfIsThreeBest(list, submission, score), which checks whether the post becomes a best candidate or not and stores it in bestThreeCandidates accordingly. When there are no contained comments in the matched post, the post is inserted into candidatesWithNilScore. The implementation of the query is shown in Listing 1. 3.2 Task 2: Most Influential Comments The goal in the second task is to obtain the most influential comments by using a metric over the groups of users that liked a particular comment. Groups of users who liked a comment are defined as the equivalence classes induced by the friendship relation. This amounts to defining strongly connected components in the graph where the nodes are users who liked a comment and the bidirectional edges are friendship links. The search of strongly connected components has been implemented using depth-first search, an idea originally introduced in [HT73], as shown in Appendix A.3. The expression val fc = new FriendComponentUtil(comment.likedBy) computes the connected components of the graph whose nodes are the set of users who liked the comment, i.e., comment.likedBy. The expression fc.components.map[c | c.size * c.size].sum then computes the sum of squares of the size of each component in the graph. Similarly to the query of task one, when the graph has nodes the score that is computed is associated with the comment and inserted into the list threeBestCandidates if its 1 new Rule ( ’ CountPosts ’) 2 . in ( ’ post ’ , SN . post ) . filter [ 3 val post = ’ post ’. fetch as Post 4 var int score = 0 5 var matches = false 6 val commentList = post . getAll Commen ts () 7 if ( commentList ?. size > 0) { 8 score = commentList . map [ c | 10 + c . likedBy . size ]. sum 9 t h r e e B e s t C a n d i d a t e s . a d d I f I s T h r e e B e s t ( post , score ) 10 matches = true 11 } else c a n d i d a t e s W i t h N i l S c o r e . add ( post ) 12 matches 13 ]. build () 14 . out ( ’ postAux ’ , SN . post , []) . build () 15 . build () Listing 1: Solution to Task One (Most Controversial Posts). 1 new Rule ( ’ U s e r C o m p o n e n t s B y C o m m e n t ’) 2 . in ( ’ comment ’ , SN . comment ) . filter [ 3 val comment = ’ comment ’. fetch as Comment 4 var score = 0 5 var matches = false 6 if ( comment . likedBy . size > 0) { 7 val fc = new F r i e n d C o m p o n e n t U t i l ( comment . likedBy ) 8 score = fc . components . map [ c | c . size * c . size ]. sum 9 t h r e e B e s t C a n d i d a t e s . a d d I f I s T h r e e B e s t ( comment , score ) 10 matches = true 11 } else c a n d i d a t e s W i t h N i l S c o r e . add ( comment ) 12 matches 13 ]. build () 14 . out ( ’ commentAux ’ , SN . comment , []) . build () 15 . build () Listing 2: Solution to Task Two (Most Influential Comments) score improves the score of any of the current best candidates using the method Util.addIfIsThreeBest(list, comment, score). When the graph has no elements the comment is inserted into candidatesWithNilScore. The implementation of the query is shown in Listing 2. 4 Evaluation and Conclusions According to the evaluation criteria proposed [Hin18], completeness has been addressed by considering all of the proposed models (of different sizes) and their model updates. In addition, the solutions pass the correctness criteria defined in the benchmark framework test suite. Below we discuss the rest of the proposed evaluation criteria. 4.1 Understandability and Conciseness YAMTL is a MT language that does not provide implicit support for incremental query evaluation at present. However, given that queries can be defined as rule filter conditions and that model transformations can be executed incrementally, YAMTL provides all the ingredients that are required to solve the proposed case. This means that the solution introduces some noise by (1) enabling universal quantification using transformation rules, whose output pattern is immaterial, and (2) by storing the best candidates for each task in auxiliary data structures. On the one hand, comprehending the query, involves understanding the declaration of model-to- model transformation rules in YAMTL, which amounts to understanding how a conditional transformation rule is matched. On the other hand, the declaration of transformation rules is somewhat more verbose than other MT languages available as external DSLs (e.g., ATL). 4.2 Performance and Scalability In this section, the experimental results obtained from running the benchmark accompanying the case for the YAMTL solution are discussed and compared with the results obtained for the reference solution, NMF [Hin15]. Each solution was run in two execution modes: batch and incremental, which we denote with the suffixes -batch and -incr respectively. The results are plotted per solution (and execution mode) using three graphs (both for time and for memory usage), as shown in Appendix C: one for the results obtained in the transformation of the initial model (initial phase); one for the results obtained in the re-execution of the query after the twenty source updates (update phase); and a final one combining the results from the previous two phases (combined phase). The results were obtained from ten runs of the benchmark. For the initial phase, for each model size the median of the ten results was selected as the representative one. For the update phase, the median of the times reported per update was selected and then the medians of each update were aggregated per model size. The benchmark was run on a MacBookPro11,5 Core i7 2.5 GHz with 16 GB of RAM with .NET Core 2.1.301 and JRE (build 1.8.0_181-b13) and Java HotSpot (build 25.181-b13, mixed mode). In the initial phase, NMF-batch shows better performance for small models. However, as the size increases the distance in used time between NMF-batch and YAMTL-batch shortens. YAMTL-batch outperforms NMF-batch for models of size larger than 256 in task one, and for models of size larger than 32 in task two. On the other hand, YAMTL-incr also outperforms NMF-batch for models of size larger than 128 in task two. YAMTL-incr is several orders of magnitude more efficient than NMF-incr, probably due to the amount of caching performed during query evaluation. YAMTL’s solution uses a MT to implement the query and only caches those transformation steps that are computationally relevant. In the update phase, NMF-batch is faster than YAMTL-batch for small sizes but this time only up to size 8 for task one and up to size 2 for task two. To put results into perspective, warm up times used by the HotSpot VM and by .NET Core CLR are not considered by the benchmark and these may affect the first results in each execution. In the incremental variants, however, the opposite phenomenon is observed, YAMTL-incr outperforms NMF-incr for small sizes, up to 256 in task one and up to 128 in task two, and then NMF-incr takes over. The bottleneck in YAMTL is currently caused by the application of some deltas by the method ChangeDescription.apply() of the EMF change model API, a third-party dependency of YAMTL. When combining reported times for the initialization and update phases, the most scalable approach is YAMTL-incr, which is also the most efficient from models of size 8 in task one and from models of size 4 in task two, followed by YAMTL-batch. For smaller sizes, NMF-batch, is more efficient. Regarding memory usage, YAMTL-batch is the thriftiest of all solutions, in the three phases, closely followed by YAMTL-incr. NMF-batch shows better scalability than NFM-incr due to the absence of caching mechanisms used in incremental query re-evaluation. Acknowledgements The author would like to thank both the organizers and the participants of the tool transformation contest (TTC) 2018 for the discussion on incremental techniques. References [Bor18] Artur Boronat. Expressive and efficient model transformation with an internal dsl of xtend. In Proceedings of the 21th ACM/IEEE International Conference on MoDELS, pages 78–88. ACM, 2018. [Fou18] The Eclipse Foundation. Xtend (official web page), 2018. http://www.eclipse.org/xtend/. [Hin15] Georg Hinkel. Change propagation in an internal model transformation language. In ICMT, volume 9152, pages 3–17. LNCS, 2015. [Hin18] Georg Hinkel. The TTC 2018 Social Media Case. In Antonio Garcia-Dominguez, Georg Hinkel, and Filip Krikava, editors, Proceedings of the 11th Transformation Tool Contest, a part of the Software Technologies: Applications and Foundations (STAF 2018) federation of conferences, CEUR Workshop Proceedings. CEUR-WS.org, June 2018. [HT73] John E. Hopcroft and Robert Endre Tarjan. Efficient algorithms for graph manipulation [H] (algo- rithm 447). Commun. ACM, 16(6):372–378, 1973. [SBPM09] David Steinberg, Frank Budinsky, Marcelo Paternostro, and Ed Merks. EMF: Eclipse Modeling Framework 2.0. Addison-Wesley Professional, 2nd edition, 2009. A Auxiliary Code A.1 Common Utility Functions Used in Task 1 and Task 2 1 class Util { 2 def static getBestThree ( List < Pair < Submission , Integer > > weightedList , List < Submission > comple tionList ) { 3 val list = new ArrayList < Submission >( weightedList . map [ key ]) 4 if ( list . size <3) { 5 val num be r Of M is si n g = 3 - list . size 6 val orderedList = co mpleti onList . sortInplace ([ c1 , c2 | 7 - c1 . timestamp . compareTo ( c2 . timestamp ) 8 ]) 9 list . addAll ( orderedList . take ( n um be r Of Mi s si ng ) ) 10 } 11 list 12 } 13 14 // start with last 15 def static void a d d I f I s T h r e e B e s t ( List < Pair < Submission , Integer > > list , Submission submission , int score ) { 16 addIfIsTh r e e B e s t ( list , submission , score , list . size -1) 17 } 18 def static private void a d d I f I s T h r e e B e s t ( List < Pair < Submission , Integer > > list , Submission submission , int score , int index ) { 19 if ( index == -1) { 20 list . add (0 , submission -> score ) 21 if ( list . size > 3) 22 list . remove ( list . last ) 23 } else { 24 val element = list . get ( index ) 25 if ( element . key == submission ) { 26 list . remove ( index ) 27 addIf I s T h r e e B e s t ( list , submission , score , index -1) 28 } else { 29 if ( score == element . value ) { 30 if ( submission . timestamp . after ( element . key . timestamp ) ) { 31 // advance a position to check if there is another 32 // submission with the same value and smaller timestamp 33 if (( index ==0) || ( index >=1 && list . get ( index -1) . value == score ) ) 34 a d d I f I s T h r e e B e s t ( list , submission , score , index -1) 35 else 36 list . add ( index , submission -> score ) 37 if ( list . size > 3) 38 list . remove ( list . last ) 39 } else { 40 if ( index <2) { 41 list . add ( index +1 , submission -> score ) 42 if ( list . size > 3) 43 list . remove ( list . last ) 44 } 45 } 46 } else if ( score > element . value ) { 47 // advance a position to check if there is another 48 // submission with a smaller value 49 add I f I s T h r e e B e s t ( list , submission , score , index -1) 50 } else { 51 if ( index <2) { 52 list . add ( index +1 , submission -> score ) 53 if ( list . size > 3) 54 list . remove ( list . last ) 55 } 56 } 57 58 } 59 } 60 } 61 62 def static sum ( Iterable < Integer > list ) { 63 list . reduce [ v1 , v2 | v1 + v2 ] 64 } 65 } A.2 Task1Module: solution to task one 1 class Task1Module extends YAMTLModule { 2 val SN = S o c i a l N e t w o r k P a c k a g e . eINSTANCE 3 4 @Accessors 5 val List < Pair < Submission , Integer > > t h r e e B e s t C a n d i d a t e s = newArrayList 6 7 @Accessors 8 val List < Submission > c a n d i d a t e s W i t h N i l S c o r e = newArrayList 9 10 new () { 11 header () . in ( ’ sn ’ , SN ) . out ( ’ out ’ , SN ) 12 13 ruleStore ( newArrayList ( 14 new Rule ( ’ CountPosts ’) 15 . in ( ’ post ’ , SN . post ) 16 . filter [ 17 val post = ’ post ’. fetch as Post 18 var int score = 0 19 var matches = false 20 val commentList = post . getAll Commen ts () 21 if ( commentList ?. size > 0) { 22 score = commentList . map [ c | 10 + c . likedBy . size ]. sum 23 t h r e e B e s t C a n d i d a t e s . a d d I f I s T h r e e B e s t ( post , score ) 24 matches = true 25 } else { 26 c a n d i d a t e s W i t h N i l S c o r e . add ( post ) 27 } 28 matches 29 ] 30 . build () 31 . out ( ’ postAux ’ , SN . post , []) . build () 32 . build () 33 )) 34 } 35 36 // HELPERS 37 def getBestThree () { 38 t h r e e B e st C a n d i d a t e s . getBestThree ( c a n d i d a t e s W i t h N i l S c o r e ) 39 } 40 def static List < Comment > get AllCom ments ( Submission submission ) { 41 if ( submission . comments ?. size >0) { 42 val commentList = newArrayList 43 commentList . addAll ( submission . comments ) 44 for ( comment : submission . comments ) { 45 val list = comment . getAll Commen ts 46 if ( list !== null ) commentList . addAll ( list ) 47 } 48 commentList 49 } 50 } 51 } A.3 Task2Module: solution to task two 1 class Task2Module extends YAMTLModule { 2 val SN = S o c i a l N e t w o r k P a c k a g e . eINSTANCE 3 @Accessors 4 val List < Pair < Submission , Integer > > t h r e e B e s t C a n d i d a t e s = newArrayList 5 @Accessors 6 val List < Submission > c a n d i d a t e s W i t h N i l S c o r e = newArrayList 7 new () { 8 header () . in ( ’ sn ’ , SN ) . out ( ’ out ’ , SN ) 9 ruleStore ( newArrayList ( 10 new Rule ( ’ U s e r C o m p o n e n t s B y C o m m e n t ’) 11 . in ( ’ comment ’ , SN . comment ) 12 . filter [ 13 val comment = ’ comment ’. fetch as Comment 14 var score = 0 15 var matches = false 16 17 if ( comment . likedBy . size > 0) { 18 val fc = new F r i e n d C o m p o n e n t U t i l ( comment . likedBy ) 19 score = fc . components . map [ c | c . size * c . size ]. sum 20 t h r e e B e s t C a n d i d a t e s . a d d I f I s T h r e e B e s t ( comment , score ) 21 matches = true 22 } else { 23 c a n d i d a t e s W i t h N i l S c o r e . add ( comment ) 24 } 25 matches 26 ] 27 . build () 28 . out ( ’ commentAux ’ , SN . comment , []) . build () 29 . build () 30 )) 31 } 32 // HELPER 33 def getBestThree () { 34 t h r e e B e st C a n d i d a t e s . getBestThree ( c a n d i d a t e s W i t h N i l S c o r e ) 35 } 36 } Computation of Strongly Connected Components in Task 2: 1 class FriendComponentUtil { 2 // to know which users liked the comment 3 Set < User > userSet 4 // maps a user to its component 5 Map < User , Set < User > > u se rT o Co mp o ne n t = newHashMap 6 // set of resulting components 7 @Accessors 8 Set < Set < User > > components = newHashSet 9 // computes connected components 10 new ( List < User > userList ) { 11 // to know which users liked the comment 12 userSet = userList . toSet 13 for ( u : userList ) { 14 var comp = u se rT o Co m po ne n t . get ( u ) 15 if ( comp === null ) { // not visited 16 comp = newHashSet ( u ) 17 components . add ( comp ) 18 userT o Co mp o ne n t . put (u , comp ) 19 u . com p u t e C o m p o n e n t ( comp ) 20 } 21 } 22 } 23 // explores friends recursively until all the relevant ones are visited 24 def private void c o m p u t e C o m p o n e n t ( User u , Set < User > comp ) { 25 for ( f : u . friends ) { 26 if ( userSet . contains ( f ) ) { 27 val isAdded = comp . add ( f ) 28 if ( isAdded ) { // not visited 29 use rT o Co m po ne n t . put (f , comp ) 30 f . c o m p u t e C o m p o n e n t ( comp ) 31 } 32 } 33 } 34 } 35 } B Configuration of Batch and Incremental Variants B.1 Variant Incremental B.1.1 Class SolutionQ1: Benchmark Integration 1 class SolutionQ1 extends Solution { 2 3 new () { 4 xform = new Q1_yamtl 5 xform . stag e Up pe r Bo u nd = 1 6 xform . ex t e n t T y p e M o d i f i e r = E x t e n t T y p e M o d i f i e r . LIST 7 xform . s e l e c t e d E x e c u t i o n P h a s e s = Exec utionP hase . MATCH_ONLY 8 xform . fromRoots = false 9 xform . executionMode = ExecutionMode . INCREMENTAL 10 } 11 12 override String Initial () { 13 xform . execute () 14 ( xform as Q1_yamtl ) . c o n t r o v e r s i a l P o s t s . map [ key . id ]. join ( ’| ’) 15 } 16 17 override String Update ( String deltaName ) { 18 xform . pro pagat eDelta ( ’ sn ’ , deltaName ) 19 ( xform as Q1_yamtl ) . c o n t r o v e r s i a l P o s t s . map [ key . id ]. join ( ’| ’) 20 } 21 } B.1.2 Class SolutionQ2: Benchmark Integration 1 class SolutionQ2 extends Solution { 2 3 new () { 4 xform = new Q2_yamtl 5 xform . stag e Up pe r Bo u nd = 1 6 xform . ex t e n t T y p e M o d i f i e r = E x t e n t T y p e M o d i f i e r . LIST 7 xform . s e l e c t e d E x e c u t i o n P h a s e s = Exec utionP hase . MATCH_ONLY 8 xform . fromRoots = false 9 xform . executionMode = ExecutionMode . INCREMENTAL 10 } 11 12 override String Initial () { 13 xform . execute () 14 ( xform as Q2_yamtl ) . i n f l u e n t i a l C o m m e n t s . map [ key . id ]. join ( ’| ’) 15 } 16 17 override String Update ( String deltaName ) { 18 xform . pro pagat eDelta ( ’ sn ’ , deltaName ) 19 ( xform as Q2_yamtl ) . i n f l u e n t i a l C o m m e n t s . map [ key . id ]. join ( ’| ’) 20 } 21 22 } B.2 Variant Batch B.2.1 Class SolutionQ1: Benchmark Integration 1 class SolutionQ1 extends Solution { 2 3 new () { 4 xform = new Q1_yamtl 5 xform . stag e Up pe r Bo u nd = 1 6 xform . ex t e n t T y p e M o d i f i e r = E x t e n t T y p e M o d i f i e r . LIST 7 xform . s e l e c t e d E x e c u t i o n P h a s e s = Exec utionP hase . MATCH_ONLY 8 xform . fromRoots = false 9 } 10 11 override String Initial () { 12 xform . execute () 13 ( xform as Q1_yamtl ) . c o n t r o v e r s i a l P o s t s . map [ key . id ]. join ( ’| ’) 14 } 15 16 override String Update ( String deltaName ) { 17 ( xform as Q1_yamtl ) . c o n t r o v e r s i a l P o s t s . clear () 18 ( xform as Q1_yamtl ) . p o s t W i t h o u t S c o r e L i s t . clear () 19 xform . reset () 20 // apply source delta only 21 xform . applyDelta ( ’ sn ’ , deltaName ) 22 // transform updated source model 23 xform . execute () 24 ( xform as Q1_yamtl ) . c o n t r o v e r s i a l P o s t s . map [ key . id ]. join ( ’| ’) 25 } 26 } B.2.2 Class SolutionQ2: Benchmark Integration 1 class SolutionQ2 extends Solution { 2 3 new () { 4 xform = new Q2_yamtl 5 xform . stag e Up p er Bo u nd = 1 6 xform . ex t e n t T y p e M o d i f i e r = E x t e n t T y p e M o d i f i e r . LIST 7 xform . s e l e c t e d E x e c u t i o n P h a s e s = Exec utionP hase . MATCH_ONLY 8 xform . fromRoots = false 9 } 10 11 override String Initial () { 12 xform . execute () 13 ( xform as Q2_yamtl ) . i n f l u e n t i a l C o m m e n t s . map [ key . id ]. join ( ’| ’) 14 } 15 16 override String Update ( String deltaName ) { 17 ( xform as Q2_yamtl ) . i n f l u e n t i a l C o m m e n t s . clear () 18 ( xform as Q2_yamtl ) . c o m m e n t W i t h o u t S c o r e L i s t . clear () 19 xform . reset () 20 // apply source delta only 21 xform . applyDelta ( ’ sn ’ , deltaName ) 22 // transform updated source model 23 xform . execute () 24 ( xform as Q2_yamtl ) . i n f l u e n t i a l C o m m e n t s . map [ key . id ]. join ( ’| ’) 25 } 26 } C Experimental Results C.1 Task 1 Results C.1.1 Time Model sizes are plotted along the X axis (in logarithmic scale) and time is plotted along the Y axis (in logarithmic scale). Q1 Initial 131072 65536 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1 1 2 4 8 16 32 64 128 256 512 1024 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 1: Initial transformation time (ms, Y axis) per model size (X axis) Q1 Aggregated Updates 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1 1 2 4 8 16 32 64 128 256 512 1024 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 2: Aggregated update time (ms, Y axis) per model size (X axis) Q1 Combined 131072 65536 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1 1 2 4 8 16 32 64 128 256 512 1024 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 3: Combined time (ms, Y axis) per model size (X axis) C.1.2 Memory Model sizes are plotted along the X axis (in logarithmic scale) and memory usage is plotted along the Y axis (in logarithmic scale). Q1 Initial 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1 1 2 4 8 16 32 64 128 256 512 1024 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 4: Initial transformation memory (MB, Y axis) per model size (X axis) Q1 Aggregated Updates 128 64 32 16 8 4 2 1 1 2 4 8 16 32 64 128 256 512 1024 0.5 0.25 0.125 0.0625 0.03125 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 5: Aggregated update memory (GB, Y axis) per model size (X axis) Q1 Combined 128 64 32 16 8 4 2 1 1 2 4 8 16 32 64 128 256 512 1024 0.5 0.25 0.125 0.0625 0.03125 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 6: Combined memory (GB, Y axis) per model size (X axis) C.2 Task 2 Results C.2.1 Time Model sizes are plotted along the X axis (in logarithmic scale) and time is plotted along the Y axis (in logarithmic scale). Q2 Initial 262144 65536 16384 4096 1024 256 64 16 4 1 1 2 4 8 16 32 64 128 256 512 1024 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 7: Initial transformation time (ms, Y axis) per model size (X axis) Q2 Aggregated Updates 65536 16384 4096 1024 256 64 16 4 1 1 2 4 8 16 32 64 128 256 512 1024 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 8: Aggregated update time (ms, Y axis) per model size (X axis) Q2 Combined 262144 65536 16384 4096 1024 256 64 16 4 1 1 2 4 8 16 32 64 128 256 512 1024 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 9: Combined time (ms, Y axis) per model size (X axis) C.2.2 Memory Model sizes are plotted along the X axis (in logarithmic scale) and memory usage is plotted along the Y axis (in logarithmic scale). Q2 Initial 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1 1 2 4 8 16 32 64 128 256 512 1024 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 10: Initial transformation memory (MB, Y axis) per model size (X axis) Q2 Aggregated Updates 256 128 64 32 16 8 4 2 1 0.5 1 2 4 8 16 32 64 128 256 512 1024 0.25 0.125 0.0625 0.03125 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 11: Aggregated update memory (GB, Y axis) per model size (X axis) Q2 Combined 256 128 64 32 16 8 4 2 1 0.5 1 2 4 8 16 32 64 128 256 512 1024 0.25 0.125 0.0625 0.03125 YAMTL-batch YAMTL-incr NMF-batch NMF-incr Figure 12: Combined memory (GB, Y axis) per model size (X axis)