Browse Source

do_step is computed for internal scenario that has no cycles.

Cláudio Gomes 3 years ago
parent
commit
af843a0ddc

+ 34 - 0
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation.tests/input/canonical_generation/sample2.sa

@@ -0,0 +1,34 @@
+semantic adaptation reactive mealy OuterFMU outerFMU
+at "./path/to/FMU.fmu"
+
+	for inner fmu reactive moore NA n1
+		at "./path/to/NA.fmu"
+		with input ports Real ip(m)
+		with output ports Real op(m)
+	
+	for inner fmu reactive mealy NA n2
+		at "./path/to/NA.fmu"
+		with input ports Real ip(m)
+		with output ports Real op(m)
+	
+	for inner fmu reactive mealy NA n3
+		at "./path/to/NA.fmu"
+		with input ports Real ip(m)
+		with output ports Real op(m)
+	
+	for inner fmu reactive mealy NA n4
+		at "./path/to/NA.fmu"
+		with input ports Real ip(m)
+		with output ports Real op(m)
+	
+	for inner fmu reactive mealy NB n5
+		at "./path/to/NB.fmu"
+		with input ports Real ip1(m), Real ip2(m)
+		with output ports Real op(m)
+	
+	coupled as n1.op -> n2.op, 
+				n2.op -> n3.ip, n2.op -> n4.ip,
+				n3.op -> n5.ip1,
+				n4.op -> n5.ip2,
+				n5.op -> n1.ip
+	

+ 9 - 0
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation.tests/src/be/uantwerpen/ansymo/semanticadaptation/tests/SemanticAdaptationGeneratorTest.xtend

@@ -139,6 +139,15 @@ class SemanticAdaptationGeneratorTest extends AbstractSemanticAdaptationTest{
 			}
 		}) }
 	
+	
+	@Test def test_topologicalSort_sample2() { __generate('input/canonical_generation/sample2.sa', new IAcceptor<CompilationTestHelper.Result>(){
+			override accept(Result t) {
+				var Adaptation sa = t.resourceSet.resources.head.allContents.toIterable.filter(SemanticAdaptation).last.elements.filter(Adaptation).head
+				
+			}
+		}) }
+	
+	
 	@Test def window_SA_parseNoExceptions() { __generate('input/power_window_case_study/window_sa.BASE.sa', new IAcceptor<CompilationTestHelper.Result>(){
 			override accept(Result t) { }
 		}) }

+ 27 - 27
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation/src/be/uantwerpen/ansymo/semanticadaptation/SemanticAdaptation.xtext

@@ -33,12 +33,12 @@ AtomicFMU:
 	;
 
 InnerFMU: // slightly different syntax
-	'inner' 'fmu' type=FMUType name=ID
+	'inner' 'fmu' (reactiveness=ReactiveOrDelayed)? (machine=MooreOrMealy)? type=FMUType name=ID
 	('at' path=STRING)? // optional in case of a semantic adaptation, where we still want to specify the ports
 	('with' 'input' 'ports' inports+=Port ("," inports+=Port)* )?
 	('with' 'output' 'ports' outports+=Port ("," outports+=Port)* )?
 	;
-	
+
 CompositeFMU:
 	'fmu' type=FMUType name=ID
 	'at' path=STRING // to be generated here
@@ -171,7 +171,7 @@ ControlRule:
 	{TriggeredControlRule} 'triggered' 'by' condition=Expression |
 	{PeriodicControlRule} 'periodic' ('at' init_time=REALTYPE)? 'every' period=REALTYPE | 
 	{MultipliedControlRule} 'multiplied' multiplication=INT 'times' | 
-	{CustomControlRule} 'control' 'rules' '{' ControlRulestatements+=Statement* returnstatement=ReturnStatement '}';
+	{CustomControlRule} 'control' 'rules' '{' controlRulestatements+=Statement* returnstatement=ReturnStatement '}';
 
 AnonymousFunction: // has access to ports
 	{FunctionExpression} '{=' code=Expression '}'| 
@@ -251,7 +251,7 @@ SingleParamDeclaration:
 ;
 
 ReturnStatement:
-	'return' Expression ';';
+	'return' expr=Expression ';';
 
 BreakStatement:
 	{BreakStatement} 'break' ';';
@@ -358,33 +358,33 @@ NullLiteral:
 	value='null';
 	
 BuiltinFunction:
-	{Floor} name='floor' '(' args+=ArithmeticExpression ')' | 
-	{Ceil} name='ceil' '(' args+=ArithmeticExpression ')' | 
-	{Round} name='round' '(' args+=ArithmeticExpression (',' args+=ArithmeticExpression)? ')' |
-	{Max} name='max' '(' args+=ArithmeticExpression ',' args+=ArithmeticExpression ')' |
-	{Min} name='min' '(' args+=ArithmeticExpression ',' args+=ArithmeticExpression ')' |
-	{Abs} name='abs' '(' args=ArithmeticExpression ')' |
-	{Close} name='is_close' '(' args+=ArithmeticExpression ',' args+=ArithmeticExpression ',' args+=ArithmeticExpression ',' args+=ArithmeticExpression ')' |  // a, b, relative tolerance, absolute tolerance 
-	{IsSet} name='is_set' '(' args=Var ')' | 
-	{DoStepFun} name='do_step' '(' fmu=[FMU] ',' t=ArithmeticExpression ',' h=ArithmeticExpression ')' |
-	{GetNextInternalTimeStep} name='get_next_time_step' '(' fmu=[FMU] ')' | // is actually the time step returned by do_step(fmu), preceded by get_state and succeeded by set_state
-	{Elapsed} name='elapsed' '(' fmu=[FMU] ')' |
-	{LastExecutionTime} name='last_execution_time' '(' fmu=[FMU] ')';
+	{Floor} 'floor' '(' args+=ArithmeticExpression ')' | 
+	{Ceil} 'ceil' '(' args+=ArithmeticExpression ')' | 
+	{Round} 'round' '(' args+=ArithmeticExpression (',' args+=ArithmeticExpression)? ')' |
+	{Max} 'max' '(' args+=ArithmeticExpression (',' args+=ArithmeticExpression)* ')' |
+	{Min} 'min' '(' args+=ArithmeticExpression (',' args+=ArithmeticExpression)* ')' |
+	{Abs} 'abs' '(' args=ArithmeticExpression ')' |
+	{Close} 'is_close' '(' args+=ArithmeticExpression ',' args+=ArithmeticExpression ',' args+=ArithmeticExpression ',' args+=ArithmeticExpression ')' |  // a, b, relative tolerance, absolute tolerance 
+	{IsSet} 'is_set' '(' args=Var ')' | 
+	{DoStepFun} 'do_step' '(' fmu=[FMU] ',' t=ArithmeticExpression ',' h=ArithmeticExpression ')' |
+	{GetNextInternalTimeStep} 'get_next_time_step' '(' fmu=[FMU] ')' | // is actually the time step returned by do_step(fmu), preceded by get_state and succeeded by set_state
+	{Elapsed} 'elapsed' '(' fmu=[FMU] ')' |
+	{LastExecutionTime} 'last_execution_time' '(' fmu=[FMU] ')';
 	
 Procedure:
-	{Rollback} name='rollback' '(' fmu=[FMU] ')' |
-	{DoStep} name='do_step' '(' fmu=[FMU] ',' t=ArithmeticExpression ','  h=ArithmeticExpression ')' |
-	{Discard} name='discard' '(' args=Var ')' |
-	{SaveState} name='save_state' '(' fmu=[FMU] ')';
+	{Rollback} 'rollback' '(' fmu=[FMU] ')' |
+	{DoStep} 'do_step' '(' fmu=[FMU] ',' t=ArithmeticExpression ','  h=ArithmeticExpression ')' |
+	{Discard} 'discard' '(' args=Var ')' |
+	{SaveState} 'save_state' '(' fmu=[FMU] ')';
 	
 Var:
-	{StepSize} name='H' | // available in ctrl
-	{StepSize} name='h' | // available in mapin/mapout
-	{CurrentTime} name='t' | // available in ctrl
-	{PassedTime} name='dt' | // available in mapin/mapout
-	{ElapsedTime} name='e' | // available in ctrl
-	{Max} name='MAX' |
-	{GenericSignal} name='signal' ('[' (
+	{StepSize} 'H' | // available in ctrl
+	{MinorStepSize} 'h' | // available in mapin/mapout
+	{CurrentTime} 't' | // available in ctrl
+	{PassedTime} 'dt' | // available in mapin/mapout
+	{ElapsedTime} 'e' | // available in ctrl
+	{Max} 'MAX' |
+	{GenericSignal} 'signal' ('[' (
 		index=NOW | 
 		index=PREV | 
 		index=INTTYPE |

+ 22 - 2
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation/src/be/uantwerpen/ansymo/semanticadaptation/formatting2/SemanticAdaptationFormatter.xtend

@@ -5,14 +5,14 @@ package be.uantwerpen.ansymo.semanticadaptation.formatting2
 
 import be.uantwerpen.ansymo.semanticadaptation.generator.Log
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Adaptation
+import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.CompositeOutputFunction
+import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.CustomControlRule
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.DataRule
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.SemanticAdaptation
-import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.SemanticAdaptationPackage
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.StateTransitionFunction
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Statement
 import org.eclipse.xtext.formatting2.AbstractFormatter2
 import org.eclipse.xtext.formatting2.IFormattableDocument
-import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.CompositeOutputFunction
 
 class SemanticAdaptationFormatter extends AbstractFormatter2 {
 	
@@ -63,6 +63,12 @@ class SemanticAdaptationFormatter extends AbstractFormatter2 {
 			}
 		}
 		
+		if (sa.control !== null){
+			sa.control.prepend[newLine]
+			
+			sa.control.rule.format
+		}
+		
 		
 		/*
 		for (inPort : sa.inports){
@@ -89,6 +95,20 @@ class SemanticAdaptationFormatter extends AbstractFormatter2 {
 		Log.pop("Formatting DataRule")	
 	}
 	
+	def dispatch void format(CustomControlRule rule, extension IFormattableDocument document){
+		Log.push("Formatting CustomControlRule")
+		
+		rule.prepend[newLine]
+		
+		for (statement : rule.controlRulestatements){
+			statement.format
+		}
+		
+		rule.returnstatement.prepend[newLine]
+		
+		Log.pop("Formatting CustomControlRule")	
+	}
+	
 	def dispatch void format(StateTransitionFunction function, extension IFormattableDocument document){
 		Log.push("Formatting StateTransitionFunction")
 		

+ 155 - 3
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation/src/be/uantwerpen/ansymo/semanticadaptation/generator/SemanticAdaptationCanonicalGenerator.xtend

@@ -3,12 +3,16 @@
  */
 package be.uantwerpen.ansymo.semanticadaptation.generator
 
+import be.uantwerpen.ansymo.semanticadaptation.generator.graph.DirectedGraph
+import be.uantwerpen.ansymo.semanticadaptation.generator.graph.FMUGraph
+import be.uantwerpen.ansymo.semanticadaptation.generator.graph.TopologicalSort
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Adaptation
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.BoolLiteral
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.BuiltinFunction
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Close
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.CompositeOutputFunction
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Connection
+import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.CustomControlRule
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.DataRule
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Declaration
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.DeclaredParameter
@@ -19,14 +23,16 @@ import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.InnerFMUDeclar
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.InnerFMUDeclarationFull
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.IntLiteral
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.IsSet
+import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.MooreOrMealy
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.OutputFunction
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Port
+import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.ReactiveOrDelayed
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.RealLiteral
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.SemanticAdaptation
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.SemanticAdaptationFactory
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.SingleParamDeclaration
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.SingleVarDeclaration
-import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.StateTransitionFunction
+import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Statement
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.StringLiteral
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Unity
 import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Variable
@@ -41,7 +47,6 @@ import org.eclipse.xtext.EcoreUtil2
 import org.eclipse.xtext.generator.AbstractGenerator
 import org.eclipse.xtext.generator.IFileSystemAccess2
 import org.eclipse.xtext.generator.IGeneratorContext
-import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.Statement
 
 /**
  * Generates code from your model files on save.
@@ -250,11 +255,158 @@ class SemanticAdaptationCanonicalGenerator extends AbstractGenerator {
 		
 		removeBindings(internalOutputPort2ExternalPortBindings, sa)
 		
-		
+		if (sa.control === null){
+			sa.control = SemanticAdaptationFactory.eINSTANCE.createControlRuleBlock()
+			sa.control.rule = SemanticAdaptationFactory.eINSTANCE.createCustomControlRule()
+			
+			createCoSimStepInstructions(sa)
+		}
 		
 		Log.pop("Canonicalize")
 	}
 	
+	def createCoSimStepInstructions(Adaptation sa) {
+		check((sa.inner as InnerFMUDeclarationFull).fmus.size > 0, "At least one internal FMU is expected...")
+		if (onlyOneInternalFMU(sa)){
+			createCosimStepForOneFMU(sa)
+		} else {
+			createCosimStepForMultipleFMUs(sa)
+		}
+	}
+	
+	def createCosimStepForOneFMU(Adaptation sa) {
+		Log.push("createCosimStepForOneFMU")
+		val fmu = (sa.inner as InnerFMUDeclarationFull).fmus.head
+		val controlRule = sa.control.rule as CustomControlRule
+		val returnedStepVar = controlRule.appendDoStep(fmu)
+		val stepVarSingleton = new LinkedList()
+		stepVarSingleton.add(returnedStepVar)
+		controlRule.appendReturnCosimStep(stepVarSingleton)
+		Log.pop("createCosimStepForOneFMU")
+	}
+	
+	def appendReturnCosimStep(CustomControlRule rule, LinkedList<SingleVarDeclaration> stepVariables) {
+		Log.push("appendReturnCosimStep")
+		
+		if (stepVariables.size == 0){
+			throw new Exception("Does not make sense. At least one inner FMU is expected.")
+		}
+		
+		val minExpression = SemanticAdaptationFactory.eINSTANCE.createMin()
+		
+		Log.println("Creating min(...) expression with the following arguments:")
+		
+		for(varDecl : stepVariables){
+			val varRef = SemanticAdaptationFactory.eINSTANCE.createVariable()
+			varRef.ref = varDecl
+			minExpression.args.add(varRef)
+			Log.println(varDecl.name)
+		}
+		
+		rule.returnstatement = SemanticAdaptationFactory.eINSTANCE.createReturnStatement()
+		rule.returnstatement.expr = minExpression
+		
+		Log.pop("appendReturnCosimStep")
+	}
+	
+	def appendDoStep(CustomControlRule rule, InnerFMU fmu) {
+		Log.push("appendDoStep")
+		val t = SemanticAdaptationFactory.eINSTANCE.createCurrentTime()
+		val H = SemanticAdaptationFactory.eINSTANCE.createStepSize()
+		
+		val doStep = SemanticAdaptationFactory.eINSTANCE.createDoStepFun()
+		doStep.t = t
+		doStep.h = H
+		doStep.fmu = fmu
+		
+		val step_var = SemanticAdaptationFactory.eINSTANCE.createSingleVarDeclaration()		
+		step_var.name = "H_" + fmu.name
+		step_var.expr = doStep
+		
+		val step_decl = SemanticAdaptationFactory.eINSTANCE.createDeclaration()
+		step_decl.declarations.add(step_var)
+		
+		rule.controlRulestatements.add(step_decl)
+		
+		Log.println("Added var " + step_var.name + " := doStep(t, H, " + fmu.name + ")")
+		
+		Log.pop("appendDoStep")
+		return step_var
+	}
+	
+	def createCosimStepForMultipleFMUs(Adaptation sa) {
+		Log.push("createCosimStepForMultipleFMUs")
+		val innerDeclaration = (sa.inner as InnerFMUDeclarationFull)
+		val controlRule = sa.control.rule as CustomControlRule
+		
+		val stepVarSingleton = new LinkedList()
+		for (fmu : innerDeclaration.topologicalSort()){
+			// TODO: Maybe add the setting of inputs and outputs here.
+			val returnedStepVar = controlRule.appendDoStep(fmu)
+			stepVarSingleton.add(returnedStepVar)
+		}
+		
+		controlRule.appendReturnCosimStep(stepVarSingleton)
+		Log.pop("createCosimStepForMultipleFMUs")
+	}
+	
+	def topologicalSort(InnerFMUDeclarationFull scenario){
+		Log.push("topologicalSort")
+		
+		val DirectedGraph<InnerFMU> inner_fmu_graph = createFMUGraph(scenario)
+		
+		val result = TopologicalSort.sort(inner_fmu_graph)
+		
+		Log.println("Sorting: " + result)
+		
+		Log.pop("topologicalSort")
+		return result
+	}
+	
+	def createFMUGraph(InnerFMUDeclarationFull scenario) {
+		Log.push("createFMUGraph")
+		
+		val graph = new FMUGraph()
+		
+		for (fmu : scenario.fmus){
+			graph.addNode(fmu)
+		}
+		
+		for (connection : scenario.connection){
+			check(connection.src.port.eContainer instanceof InnerFMU && 
+				connection.tgt.port.eContainer instanceof InnerFMU, "Weird connection found: " + connection)
+			if (reactiveMealyFMU(connection.tgt.port.eContainer as InnerFMU)){
+				graph.addEdge(connection.src.port.eContainer as InnerFMU, connection.tgt.port.eContainer as InnerFMU)
+			} else {
+				Log.println("FMU " + (connection.tgt.port.eContainer as InnerFMU).name + " is not reactive mealy, so it has no algebraic dependencies.")
+			}
+		}
+		
+		Log.println(graph.toString())
+		
+		Log.pop("createFMUGraph")
+		return graph
+	}
+	
+	def reactiveMealyFMU(InnerFMU fmu) {
+		return fmu.reactiveness == ReactiveOrDelayed.REACTIVE && fmu.machine == MooreOrMealy.MEALY
+	}
+	
+	def check(Boolean condition, String msg){
+		if (! condition){
+			throw new Exception("Assertion error: " + msg)
+		}
+	}
+	
+	def onlyOneInternalFMU(Adaptation sa) {
+		if(sa.inner instanceof InnerFMUDeclarationFull){
+			return (sa.inner as InnerFMUDeclarationFull).fmus.size == 1
+		} else {
+			throw new Exception('This kind of internal scenario is not supported yet.')
+		}
+	}
+	
+	
 	def transitiveStep(HashMap<Port, Port> internalOutputPort2ExternalPortBindings, HashMap<Port, SingleVarDeclaration> externalOutputPort2OutVarDeclaration) {
 		Log.push("transitiveStep")
 		

+ 143 - 0
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation/src/be/uantwerpen/ansymo/semanticadaptation/generator/graph/DirectedGraph.java

@@ -0,0 +1,143 @@
+package be.uantwerpen.ansymo.semanticadaptation.generator.graph;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.eclipse.xtext.util.Strings;
+
+/*****************************************************************************
+ * File: DirectedGraph.java
+ * Author: Keith Schwarz (htiek@cs.stanford.edu)
+ *
+ * A class representing a directed graph.  Internally, the class is represented
+ * by an adjacency list.
+ */
+
+public class DirectedGraph<T> implements Iterable<T> {
+    /* A map from nodes in the graph to sets of outgoing edges.  Each
+     * set of edges is represented by a map from edges to doubles.
+     */
+    private final Map<T, Set<T>> mGraph = new HashMap<T, Set<T>>();
+
+    /**
+     * Adds a new node to the graph.  If the node already exists, this
+     * function is a no-op.
+     *
+     * @param node The node to add.
+     * @return Whether or not the node was added.
+     */
+    public boolean addNode(T node) {
+        /* If the node already exists, don't do anything. */
+        if (mGraph.containsKey(node))
+            return false;
+
+        /* Otherwise, add the node with an empty set of outgoing edges. */
+        mGraph.put(node, new HashSet<T>());
+        return true;
+    }
+
+    /**
+     * Given a start node, and a destination, adds an arc from the start node 
+     * to the destination.  If an arc already exists, this operation is a 
+     * no-op.  If either endpoint does not exist in the graph, throws a 
+     * NoSuchElementException.
+     *
+     * @param start The start node.
+     * @param dest The destination node.
+     * @throws NoSuchElementException If either the start or destination nodes
+     *                                do not exist.
+     */
+    public void addEdge(T start, T dest) {
+        /* Confirm both endpoints exist. */
+        if (!mGraph.containsKey(start) || !mGraph.containsKey(dest))
+            throw new NoSuchElementException("Both nodes must be in the graph.");
+
+        /* Add the edge. */
+        mGraph.get(start).add(dest);
+    }
+
+    /**
+     * Removes the edge from start to dest from the graph.  If the edge does
+     * not exist, this operation is a no-op.  If either endpoint does not
+     * exist, this throws a NoSuchElementException.
+     *
+     * @param start The start node.
+     * @param dest The destination node.
+     * @throws NoSuchElementException If either node is not in the graph.
+     */
+    public void removeEdge(T start, T dest) {
+        /* Confirm both endpoints exist. */
+        if (!mGraph.containsKey(start) || !mGraph.containsKey(dest))
+            throw new NoSuchElementException("Both nodes must be in the graph.");
+
+        mGraph.get(start).remove(dest);
+    }
+
+    /**
+     * Given two nodes in the graph, returns whether there is an edge from the
+     * first node to the second node.  If either node does not exist in the
+     * graph, throws a NoSuchElementException.
+     *
+     * @param start The start node.
+     * @param end The destination node.
+     * @return Whether there is an edge from start to end.
+     * @throws NoSuchElementException If either endpoint does not exist.
+     */
+    public boolean edgeExists(T start, T end) {
+        /* Confirm both endpoints exist. */
+        if (!mGraph.containsKey(start) || !mGraph.containsKey(end))
+            throw new NoSuchElementException("Both nodes must be in the graph.");
+
+        return mGraph.get(start).contains(end);
+    }
+
+    /**
+     * Given a node in the graph, returns an immutable view of the edges
+     * leaving that node as a set of endpoints.
+     *
+     * @param node The node whose edges should be queried.
+     * @return An immutable view of the edges leaving that node.
+     * @throws NoSuchElementException If the node does not exist.
+     */
+    public Set<T> edgesFrom(T node) {
+        /* Check that the node exists. */
+        Set<T> arcs = mGraph.get(node);
+        if (arcs == null)
+            throw new NoSuchElementException("Source node does not exist.");
+
+        return Collections.unmodifiableSet(arcs);
+    }
+
+    /**
+     * Returns an iterator that can traverse the nodes in the graph.
+     *
+     * @return An iterator that traverses the nodes in the graph.
+     */
+    public Iterator<T> iterator() {
+        return mGraph.keySet().iterator();
+    }
+
+    /**
+     * Returns the number of nodes in the graph.
+     *
+     * @return The number of nodes in the graph.
+     */
+    public int size() {
+        return mGraph.size();
+    }
+
+    /**
+     * Returns whether the graph is empty.
+     *
+     * @return Whether the graph is empty.
+     */
+    public boolean isEmpty() {
+        return mGraph.isEmpty();
+    }
+    
+}

+ 21 - 0
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation/src/be/uantwerpen/ansymo/semanticadaptation/generator/graph/FMUGraph.java

@@ -0,0 +1,21 @@
+package be.uantwerpen.ansymo.semanticadaptation.generator.graph;
+
+import java.util.Iterator;
+
+import be.uantwerpen.ansymo.semanticadaptation.semanticAdaptation.InnerFMU;
+
+public class FMUGraph extends DirectedGraph<InnerFMU> {
+
+    @Override
+    public String toString(){
+    	String result = "";
+    	Iterator<InnerFMU> iterator = this.iterator();
+    	while(iterator.hasNext()){
+    		InnerFMU srcNode = iterator.next();
+    		for (InnerFMU tgtNode : this.edgesFrom(srcNode)) {
+    			result += srcNode.getName() + "-->" + tgtNode.getName() + '\n';    			
+			}
+    	}
+		return result;
+    }
+}

+ 143 - 0
DSL_SemanticAdaptation/be.uantwerpen.ansymo.semanticadaptation/src/be/uantwerpen/ansymo/semanticadaptation/generator/graph/TopologicalSort.java

@@ -0,0 +1,143 @@
+package be.uantwerpen.ansymo.semanticadaptation.generator.graph;
+
+/******************************************************************************
+ * File: TopologicalSort.java
+ * Author: Keith Schwarz (htiek@cs.stanford.edu)
+ *
+ * A linear-time algorithm for computing a topological sort of a directed
+ * acyclic graph.  A topological sort is an ordering of the nodes in a graph
+ * such that for each node v, all of the ancestors of v appear in the ordering
+ * before v itself.  Topological sorting is useful, for example, when computing
+ * some function on a DAG where each node's value depends on its ancestors.
+ * Running a topological sort and then visiting the nodes in the order
+ * specified by this sorted order ensures that the necessary values for each
+ * node are available before the node is visited.
+ *
+ * There are several algorithms for computing topological sorts.  The one used
+ * here was first described in "Edge-Disjoint Spanning Trees and Depth-First
+ * Search" by Robert Tarjan.  The algorithm is reminiscent of Kosaraju's SCC
+ * algorithm.  We begin by constructing the reverse graph G^{rev} from the
+ * source graph, then running a depth-first search from each node in the graph.
+ * Whenever we finish expanding a node, we add it to a list of visited nodes.
+ * The intution behind this algorithm is that a DFS in the reverse graph will
+ * visit every node that is an ancestor of the given node before it finishes
+ * expanding out any node.  Since those nodes will be added to the sorted order
+ * before the expanded node, we have the desired property of the topological
+ * sort.
+ *
+ * This process can be augmented to detect a cycle in the original graph.  As
+ * we do the search, we'll maintain a set of nodes that we have visited and a
+ * set of nodes that we have expanded.  If when doing the DFS we find a node
+ * that has been visited but not expanded, it means that we have encountered a
+ * cycle in the graph.  Moreover, if a cycle exists, we know that this will
+ * occur, since the first time any node in the cycle is visited the DFS will
+ * expand out the cycle.
+ */
+
+import java.util.*; // For List, Map.
+
+public final class TopologicalSort {
+    /**
+     * Given a directed acyclic graph, returns a topological sorting of the
+     * nodes in the graph.  If the input graph is not a DAG, throws an
+     * IllegalArgumentException.
+     *
+     * @param g A directed acyclic graph.
+     * @return A topological sort of that graph.
+     * @throws IllegalArgumentException If the graph is not a DAG.
+     */
+    public static <T> List<T> sort(DirectedGraph<T> g) {
+        /* Construct the reverse graph from the input graph. */
+        DirectedGraph<T> gRev = reverseGraph(g);
+
+        /* Maintain two structures - a set of visited nodes (so that once we've
+         * added a node to the list, we don't label it again), and a list of
+         * nodes that actually holds the topological ordering.
+         */
+        List<T> result = new ArrayList<T>();
+        Set<T> visited = new HashSet<T>();
+
+        /* We'll also maintain a third set consisting of all nodes that have
+         * been fully expanded.  If the graph contains a cycle, then we can
+         * detect this by noting that a node has been explored but not fully
+         * expanded.
+         */
+        Set<T> expanded = new HashSet<T>();
+
+        /* Fire off a DFS from each node in the graph. */
+        for (T node: gRev)
+            explore(node, gRev, result, visited, expanded);
+
+        /* Hand back the resulting ordering. */
+        return result;
+    }
+
+
+    /**
+     * Recursively performs a DFS from the specified node, marking all nodes
+     * encountered by the search.
+     *
+     * @param node The node to begin the search from.
+     * @param g The graph in which to perform the search.
+     * @param ordering A list holding the topological sort of the graph.
+     * @param visited A set of nodes that have already been visited.
+     * @param expanded A set of nodes that have been fully expanded.
+     */
+    private static <T> void explore(T node, DirectedGraph<T> g,
+                                    List<T> ordering, Set<T> visited,
+                                    Set<T> expanded) {
+        /* Check whether we've been here before.  If so, we should stop the
+         * search.
+         */
+        if (visited.contains(node)) {
+            /* There are two cases to consider.  First, if this node has
+             * already been expanded, then it's already been assigned a
+             * position in the final topological sort and we don't need to
+             * explore it again.  However, if it hasn't been expanded, it means
+             * that we've just found a node that is currently being explored,
+             * and therefore is part of a cycle.  In that case, we should 
+             * report an error.
+             */
+            if (expanded.contains(node)) return;
+            throw new IllegalArgumentException("Graph contains a cycle.");
+        }
+        
+        /* Mark that we've been here */
+        visited.add(node);
+
+        /* Recursively explore all of the node's predecessors. */
+        for (T predecessor: g.edgesFrom(node))
+            explore(predecessor, g, ordering, visited, expanded);
+
+        /* Having explored all of the node's predecessors, we can now add this
+         * node to the sorted ordering.
+         */
+        ordering.add(node);
+
+        /* Similarly, mark that this node is done being expanded. */
+        expanded.add(node);
+    }
+
+    /**
+     * Returns the reverse of the input graph.
+     *
+     * @param g A graph to reverse.
+     * @return The reverse of that graph.
+     */
+    private static <T> DirectedGraph<T> reverseGraph(DirectedGraph<T> g) {
+        DirectedGraph<T> result = new DirectedGraph<T>();
+
+        /* Add all the nodes from the original graph. */
+        for (T node: g)
+            result.addNode(node);
+
+        /* Scan over all the edges in the graph, adding their reverse to the
+         * reverse graph.
+         */
+        for (T node: g)
+            for (T endpoint: g.edgesFrom(node))
+                result.addEdge(endpoint, node);
+
+        return result;
+    }
+}