Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: for the plan.

Jump to: navigation, search


Release Notes for VIATRA2 R3.1

General information

VIATRA2 R3.1 is the previous release of the VIATRA2 model transformation framework.

What's new

Transformation engine updates

The VIATRA transformation engine has been further improved with a number of bug fixes and structural refactoring operations. The engine now provides some execution feedback (number of atomic VIATRA operations interpreted).

Updated exception handling and feedback

The exception system of the engine has been re-designed to give more user-friendly information about runtime failures. All stack traces are now VTCL-specific, which means that it's easy to locate on which VTCL instruction the error happened. Exceptions, stack traces, and other useful information are printed to the standard output as well as the new VIATRA2 Console GUI component.

Pattern matching features

Updated incremental pattern matching

Incremental pattern matching is a technique where all matches of a pattern are cached, so that they can be retrieved instantly; this may improve the performance of repeated pattern queries dramatically. As a downside, these caches can take up a considerable amount of memory. Additionally, they need to be kept continuously up-to-date; this happens automatically and incrementally whenever the model is updated, but it imposes an overhead on model manipulation. The incremental pattern matcher is expected to perform better if complex patterns are frequently queried, without extensive model changes inbetween.

By default, all patterns are matched using the traditional local search based pattern matcher. Since Release 3, you may prepend the annotation @incremental to the machine declaration to have the incremental pattern matcher module handle the pattern matching for all patterns; this can often significantly improve performance. You can also fine-tune performance by overriding the choice on a per-pattern basis, by annotating the pattern definition by @incremental or @localsearch. Please also note that pattern calls issued by incrementally matched patterns will be incorporated using the incremental pattern matcher in all cases, regardless of the specified pattern matcher of the called pattern. On the other hand, local search patterns may call incrementally matched patterns and take advantage of the incrementally maintained caches.

Since R3.1, the incremental pattern matcher has parallel processing capabilities. In the VIATRA2 preferences, you can set the number of parallel threads available to the pattern matcher. The default is 0, which is conventional sequential execution. If 1 is specified, the cache maintenance is run on a separate concurrent thread, which may or may not improve performance on multi-core architectures. As an experimental feature, a setting of n (where n>1) will cause the cache maintenace itself to be executed on n parallel threads, while a setting of auto will try to guess the right number of threads.

Functional differences between LS and INC

Feature Local search Incremental Remarks
Locally undefined parameters Input only Not supported A parameter is undefined if it does not appear in an entity or relation assertion or a positive pattern call, e.g. pattern big(A) = {check(A>4);}
Find in check Supported Not supported E.g check(find otherPattern(A,B));
Native functions in check Any Deterministic Deterministic means that the value should be a deterministic function of the actual parameters (including name or value in case of model elements).
Recursion Finds least fixpoint Finds one fixpoint The latter one is weaker, but equivalent if stratifiable (e.g DAG-like match justification, not necessarily DAG-like pattern call graph)
Negative recursion Not supported Supported Negative recursion is through a chain of pattern calls containing at least one negative.
Match counting Not supported Supported e.g. find otherPattern(A,B,C) # N; in a pattern

Undefined variables. For some clarification, negative application conditions or check expressions do not 'define' a variable. In fact, using undefined variables in negative calls is an important feature (they indicate 'running variables' of the NAC), but these undefined variables cannot be used as parameters of the pattern with INC, and only as input with LS. Example:

 // Matches nodes that do not have neighbours. 
 // Here OtherNode is locally undefined, but not a parameter of 'isolated', therefore both PMs are fine. 
 // Semantics: there does not exist any OtherNode such that neighbour(Node, OtherNode) holds for Node.
 pattern isolated(Node) = {
  neg find neighbour(Node, OtherNode);
 // Matches nodes that are not the neighbours of OtherNode. 
 // Here OtherNode is a locally undefined parameter. Therefore using LS is mandatory and OtherNode has to be given on input.
 // If you really want to use INC, please add at least 'node(OtherNode);'
 pattern notNeighbours(Node, OtherNode) = {
  neg find neighbour(Node, OtherNode);

Deterministic native functions. INC assumes that native functions used in check() expressions always evaluate to the same value with the same set of inputs. This is a neccessity of the philosophy of incremental PM. For example, a hypotetical random() native function is not deterministic, it would be a bad idea to cache its value using INC. currentDate() or readSomeValueFromSomewhereElseInTheModelSpaceAndAddItTo(A) are also bad choices, as they depend on factors external to their arguments (the current date and some other value in the model space, respectively). power(Base, Exponent) is fine, as it only depend on the Base and the Exponent, and it will always evaluate to the same result if called with the same arguments. If a native function argument is a model element, the function is explicitly allowed to use the name or value; if one of these change, the check() expression will be re-evaluated.

Stratifiable recursion. Stratifiable recursion implies that INC finds the least fixed point. We call recursion stratifiable iff actual match tuples of patterns are partially ordered by the 'find' justification between them. Recursion along a tree or DAG graph is stratifiable, whereas recursion along the edges of an arbitrary graph is not, and can lead to unexpected results with RETE.

Transformation language features


  • As mentioned above, @incremental and @localsearch can be applied to patterns or machines in order to locally or globally select a pattern matcher module.
  • As in the previous release, you can use the @Trigger in front of graph transformation rules to create a "live" (or event-driven) rule. A simple example, where some textual output is emitted whenever a new entity is created in the modelspace, is shown here:
 gtrule testTrigger(inout E) =
   // This pattern triggers the action sequence.
   precondition pattern lhs(E)=

   // The action sequence.
     println( "Trigger Action triggered by: " + E );

For further reference, check out VIATRA2/Live_Transformations.

Injectivity semantics, shareable patterns and variable equals/does-not-equal clauses in patterns

Both features are detailed in VIATRA2/Examples/VTCL/GraphPattern

Experimental transformation language features

Type checking

Type checking is an experimental feature of the VTCL language. You can find more information here: VIATRA2/Examples/VTCL/TypeChecking

Random choose

The annotation @Random on a pattern will cause choose constructs to select a pseudorandomly selected match instead of an arbitrary one. Currently it does not affect forall.

OrderBy forall

As a preview feature, the iteration order of forall loops can be specified. Syntax is subject to change. The current way to do it is to use the @OrderBy annotation on the pattern that is matched by forall. The annotation parameter 'col' is the integer index of the symbolic parameter by which the results should be ordered, starting from 0. The value of annotation parameter 'type' specifies how that parameter should be interpreted and ordered:

  • 'name' orders entities by name, case sensitive
  • 'name-nocase' is the same, but ignores case
  • 'fqn' orders entities by fully qualified name, case sensitive
  • 'fqn-nocase' is the same, but ignores case
  • 'integer' orders entities by value, interpreted as integer
  • 'double' orders entities by value, interpreted as double
  • 'nocase' or 'value-nocase' orders entities by value, as a string, ignoring case
  • if omitted, entities are ordered by value (as a string), and natural comparison is used on Comparable native types (java.lang.Integer, etc.)

Finally, the annotation parameter 'order' will reverse the order if set to 'desc'.

Example: the following code treats people in order of age, starting by the oldest.

 @orderby('col'='1', 'type'='integer', 'order'='desc')
 pattern personWithAge(Person, Age) = {
  person.hasAge(HA, Person, Age);
 forall P, A with find personWithAge(P, A) do ...

Matching set counting

As an experimental feature, patterns may call other patterns to count how many ways they can match. Syntax:

 find somePattern(PassedParameter, QuantifiedParameter, OtherPassedParameter, OtherQuantifiedParameter, ...) # Count

Semantics: the variable Count will hold the native (non-model element) integer value of how many ways the quantified variables can be substituted to form a match of {{{somePattern}}}, while some other parameters of the called pattern may be bound to variables of this match of the calling pattern. Quantified variables are exactly those that do not appear anywhere else in the pattern, just like the quantified variables of a negative pattern call. In fact, the negative pattern call can be regarded as a special case of match counting, with the Count bound to zero. Non-quantified variables have to be defined by at least one pattern construct (entity, relation, positive pattern call); it is not allowed for them to only appear in other match set counters, negative pattern calls, check expressions, or as a symbolic parameter.


 pattern unhappy(Person) = {
  find friend(Person, Friend) # NumberOfFriends;
  find enemy(Person, Enemy) # NumberOfEnemies;
  check(NumberOfFriends < NumberOfEnemies);

GUI features

Mainstream GUI updates

  • Parameter passing, transformation launcher dialog: the launcher dialog for GTASM machines has been redesigned to allow for a better way of passing modelspace references as parameters. The dialog displays the expected parameters, provides feedback on the correct number of parameters specified, and also supports history by pressing Ctrl+Space. Double click any element in the tree to provides its FQN as a parameter.
  • VTCL editor improvements: the VTCL editor now supports the new VTCL keywords. Press Alt+P to quick parse a VTCL file into an active model space. If you have multiple model spaces open, a dialog will appear in which you may select which modelspace you want the VTCL to be attached to. Saving a VTCL file will also automatically trigger quick parsing. The LPG-based parser will provide in-editor error and warning messages.
  • drag-and-drop: the bugs in the drag-and-drop code have been fixed, which means you can always use drag and drop from the navigator view to import external model files (the best importer will be selected based on the file's extension), merge VPML files, and parse VTCL files.

Textual output updates

  • Multiple output buffers, writing to files: the textual output buffers view has been updated, to support the new multiple output buffers feature. By this, a VTCL program may simultaneously write to multiple in-memory and file-based output buffers, making it substantially easier to write code generating transformations. A simple example in VTCL making use of this:
 rule main() = seq
  let Buf0 = getBuffer("file://test4/dir/dir2/file.ext") in
  let Buf1 = getBuffer("core://test7") in
   println(Buf0, "This will go into a file");
   println(Buf1, "Hello buffered world!"); 
  println("This goes to the default output too!");

Here, the build-in getBuffer function is used to access output buffers. The file:// buffers are designated with a workspace-relative path (project/folder/subfolder/file.ext), and are automatically created with the full path if not existent yet. core:// buffers are in-memory containers which are shown on the GUI. The clearBuffer() native function may be used to clear the contents of *core* buffers.

VIATRA Console

The VIATRA Console is a new addition to the VIATRA Perspective. It provides feedback about debug, warning, info and error messages, and is a very useful feature to the VIATRA transformation developer. Stack traces of runtime VIATRA exceptions, error messages from importers, detailed error messages from the VTCL parser etc all appear here. For performance considerations, it is recommended to turn "debug" messages off.

Modelspace visualisation

The new release adds the option to display (a part of) a model space outside the tree editor as a graph. The created graph view can be populated using different layouting algorithms and is repopulated in case of changes in the displayed parts of the modelspace.

This feature relies on version 1.1 of the Eclipse Zest library (available with Eclipse 3.5, Galileo).

Back to the top