Skip to main content
Jump to: navigation, search


Revision as of 17:13, 6 October 2015 by (Talk | contribs) (Transformation rules)

Usage of the VIATRA-DSE API

This page presents the basic API of the VIATRA-DSE framework. After reading this you will be able to define a DSE problem including transformation rules and objectives, run it with different built in strategies and use the solutions the exploration found.

It is highly recommended to get familiar with the EMF and EMF-IncQuery frameworks first as they are essential for using VIATRA-DSE.


VIATRA-DSE depends on the Eclipse platform thus using it one should create a plug-in project and add the org.eclipse.viatra.dse.base plug-in as a dependency for the plug-in project. To start using the framework type this line of code:

DesignSpaceExplorer dse = new DesignSpaceExplorer();

Defining the domain

The first thing required is a metamodel or ecore model created with EMF and an initial model. See the code below.

EObject root = createInitialModel();

The initial model must be set as the root EObject of the model (i.e.: it contains all other objects of the model via the containment hierarchy). In the future Resource and ResourceSet may be supported as well.

Transformation rules

Rules tell the engine how the initial model can be modified and treated as atomic steps. They consist of a left hand side (LHS or condition) and a right hand side (RHS). The LHS is always an IncQuery pattern (i.e. a model query) while the RHS is simple Java code. To define such a rule an instance of the DSETransformationRule class must be created.

Let's say a pattern named myPattern with two parameters is already available and can be used as a LHS.

DSETransformationRule<MyPatternMatch, MyPatternMatcher> rule = 
    new DSETransformationRule<MyPatternMatch, MyPatternMatcher>(
        MyPatternQuerySpecification.instance(), new MyPatternMatchProcessor() {
        public void process(ExampleEObject p1, ExampleEObject p2) {
            // RHS manipulates the EMF model

Note 1: A rule can also have a name via the "setName(String)" method.

Note 2: It's forbidden to apply two rules with the same LHS to be able to distinguish them based on the pattern. If two rules would use the same LHS, creating a new pattern and using the IncQuery keyword find can avoid code duplication.

Note 3: It's a good practice to use static factory methods in a separate class for creating rules, as the above code can worsen the readability.


Objectives determine the quality of a solution represented in double values. The goal can be a single objective with a binary check (valid or invalid model) or it can consist of multiple numerical objectives giving a multi-objective optimization problem. There are two types of objective: hard and soft. Hard objectives are used to decide whether the solution is valid and can be considered as a usable solution, while soft objectives determine the quality and enables the ordering of the solutions. It is possible to use both types or only one of them.

To use an objective one must implement the IObjective interface, the BaseObjective abstract class or use one of the built in implementations. Objectives are mainly defined by IncQuery patterns, hence the built in implementations are focused on them. ModelQueriesHardObjective is a hard objective implementation, which can be configured with IncQuery patterns. It checks if all of the given patterns have a match in the actual model, and considers it a valid solution only if they have (returning a fitness value 1 and returning 0 if it is invalid). See the following example:

dse.addObjective(new ModelQueriesHardObjective("MyHardObjective")

Calling the .withType(ModelQueryType.NO_MATCH) method will modify the behavior of the objective: it considers a solution valid if there are no matches of the given patterns.

Other built-in objective implementations are:

  • WeightedQueriesSoftObjective - is a soft objective, which can be configured with a list of patterns and weights assigned to them. It calculates a fitness value in the following way: fitness = sum(pattern[i].countMatches() * weight[i])
  • CompositHardObjective - can be configured with a set of hard objectives. It considers a solution valid, if all the hard objectives assess it as a valid solution.
  • CompositSoftObjective - can be configured with a set of soft objectives. Returns the sum of the calculated fitness by the the sub objectives.
  • TrajectoryCostSoftObjective - calculates fitness from the trajectory. Different weight for the transformation rules can be assigned.
  • NoRuleActivationsHardObjective - this hard objective is satisfied only if there are no more rule activations from the current state.

Objectives also have a comparator, which decides the relation between two solutions (which one is better) corresponding to the particular objective. For example, an objective could be a value to maximize, or a value to minimize a difference from a given number. This is dependent of the objective's comparator, which can be set in the following way:


The default comparator of the built in objective is Comparators.HIGHER_IS_BETTER.

State coding

A state coder is also necessary for the engine to start the exploration. By default there is a built-in general state coder which will be enough in most of the times but it can fail for certain problems and a custom state coder is required in order to use the framework.

To use the built-in state coder, the metamodel must be given with the following code:

// The framework will implicitly instantiate the built-in state coder if it is not set. 
dse.setStateCoderFactory(new SimpleStateCoderFactory(dse.getMetaModelPacakges()));

For any more detail about state coders please see the state coding section of this wiki.

Starting the exploration

To start the exploration, a strategy must also be defined. Static methods of the Strategies class can be used to create a built-in strategy such as depth first search. For more on these strategies see VIATRA2/DSE/UserGuide/Strategy.

// Starting the exploration process with a depth first search limited to 5 step depth.

Using the results

A solution of the exploration is a trajectory, a sequence of rules which if applied to the initial model, it satisfies the goals. It's important that the same goal state can be reached by different trajectories and this is also represented by the results: an instance of Solution class can have multiple SolutionTrajectory instances. A solution has at least one solution trajectory, but nothing more can be expected, as it is heavily depend on the traversal strategy and the actual traversal of the state space. The SolutionTrajectory can be used to transform the given model (should be the initial model) based on the trajectory.

Collection<Solution> solutions = dse.getSolutions();
if (!solutions.isEmpty()) {
    Solution solution = dse.getAllSolutions().iterator().next();
    SolutionTrajectory solutionTrajectory = solution.getArbitraryTrajectory();
    // Transform the model
// To get an arbitrary solution trajectory with simpler way, use the next method:
SolutionTrajectory solutionTrajectory = dse.getArbitrarySolution();

Other functions of the API

Global constraints

If there are any global constraints added they must be satisfied in all of the intermediate model state of trajectory. If a global constraint is not satisfied the engine won't allow the exploration to go further from that state. It can be useful to reduce the state space, but it can also be a criteria for a usable solution.

dse.addGlobalConstraint(new ModelQueriesGlobalConstraint()

Parallel execution

The engine allows the traversal strategy to explore the state space with multiple threads. By default it allows a number of threads equal to the number of logical cores in the processor. It can be overridden with the following code:


Run configuration

Running the DSE engine needs an Eclipse platform, thus it is recommended to use a JUnit plug-in test run configuration in headless mode. Here are some basic steps to configure it properly:

  • Add the org.junit as a dependency.
  • Annotate the method with @Test annotation
  • Create a JUnit plug-in test run configuration with the corresponding class
  • Locate the Run configuration / Main tab and change the selection at "Run an application" to "[No Application] – Headless Mode".
  • On the Plug-ins tab select "plug-ins selected below only" from the drop down list and select the plugin which contains the class with the annotated method.
  • Hit "Add Required Plug-ins" 2-3 times.


Bpmn example: VIATRA/DSE/UserGuide/BPMNExample

Back to the top