Quellcode durchsuchen

most of search algorithm done. Missing transforming the trigger sequence into an operation graph and running it.

Claudio Gomes vor 5 Jahren
Ursprung
Commit
cabed86b46

+ 0 - 77
HintCOEngine/instances/generated/create_default_candidate_space_test.xmi

@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="ASCII"?>
-<hintco:HintConfiguration xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:hintco="ua.ansymo.hintco">
-  <candidates identifier="Original">
-    <cosimunits xsi:type="hintco:CosimUnitInstance" identifier="Scenario" declaration="//@csuDeclarations.0">
-      <adaptation xsi:type="hintco:MultiRateAdaptation" rate="10"/>
-      <ports xsi:type="hintco:OutputPortInstance" identifier="psuvolt" valueTo="//@candidates.0/@cosimunits.2/@ports.1"/>
-      <ports xsi:type="hintco:OutputPortInstance" identifier="ref" valueTo="//@candidates.0/@cosimunits.1/@ports.2"/>
-    </cosimunits>
-    <cosimunits xsi:type="hintco:CosimUnitInstance" identifier="DLoopController_FixedEuler_1Em6" declaration="//@csuDeclarations.1">
-      <adaptation xsi:type="hintco:MultiRateAdaptation" rate="10"/>
-      <ports xsi:type="hintco:InputPortInstance" identifier="VEL_FB@expseu_" valueFrom="//@candidates.0/@cosimunits.2/@ports.4">
-        <adaptation xsi:type="hintco:XorPortAdaptation">
-          <children xsi:type="hintco:ExtrapolationAdaptation" implies="//@candidates.0/@cosimunits.1/@ports.0/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@ports.1/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@ports.2/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@adaptation" order="1"/>
-          <children xsi:type="hintco:InterpolationAdaptation" weight="10" implies="//@candidates.0/@cosimunits.1/@ports.0/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@ports.1/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@ports.2/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@adaptation" order="1"/>
-        </adaptation>
-      </ports>
-      <ports xsi:type="hintco:InputPortInstance" identifier="POS_FB@expseu_" valueFrom="//@candidates.0/@cosimunits.3/@ports.1">
-        <adaptation xsi:type="hintco:XorPortAdaptation">
-          <children xsi:type="hintco:ExtrapolationAdaptation" implies="//@candidates.0/@cosimunits.1/@ports.0/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@ports.1/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@ports.2/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@adaptation" order="1"/>
-          <children xsi:type="hintco:InterpolationAdaptation" weight="10" implies="//@candidates.0/@cosimunits.1/@ports.0/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@ports.1/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@ports.2/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@adaptation" order="1"/>
-        </adaptation>
-      </ports>
-      <ports xsi:type="hintco:InputPortInstance" identifier="REFERENCE@expseu_" valueFrom="//@candidates.0/@cosimunits.0/@ports.1">
-        <adaptation xsi:type="hintco:XorPortAdaptation">
-          <children xsi:type="hintco:ExtrapolationAdaptation" implies="//@candidates.0/@cosimunits.1/@ports.0/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@ports.1/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@ports.2/@adaptation/@children.0 //@candidates.0/@cosimunits.1/@adaptation" order="1"/>
-          <children xsi:type="hintco:InterpolationAdaptation" weight="10" implies="//@candidates.0/@cosimunits.1/@ports.0/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@ports.1/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@ports.2/@adaptation/@children.1 //@candidates.0/@cosimunits.1/@adaptation" order="1"/>
-        </adaptation>
-      </ports>
-      <ports xsi:type="hintco:OutputPortInstance" identifier="OUTPUT@expseu_" valueTo="//@candidates.0/@cosimunits.2/@ports.0"/>
-    </cosimunits>
-    <cosimunits xsi:type="hintco:CosimUnitInstance" identifier="EMAPlantNoLoad_FixedEuler_1Em6" declaration="//@csuDeclarations.2">
-      <adaptation xsi:type="hintco:MultiRateAdaptation" rate="10"/>
-      <ports xsi:type="hintco:InputPortInstance" identifier="torque_input@expseu_" valueFrom="//@candidates.0/@cosimunits.1/@ports.3">
-        <adaptation xsi:type="hintco:XorPortAdaptation">
-          <children xsi:type="hintco:ExtrapolationAdaptation" implies="//@candidates.0/@cosimunits.2/@ports.0/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.1/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.2/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.3/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@adaptation" order="1"/>
-          <children xsi:type="hintco:InterpolationAdaptation" weight="10" implies="//@candidates.0/@cosimunits.2/@ports.0/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.1/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.2/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.3/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@adaptation" order="1"/>
-        </adaptation>
-      </ports>
-      <ports xsi:type="hintco:InputPortInstance" identifier="psu_vol_input_v@expseu_" valueFrom="//@candidates.0/@cosimunits.0/@ports.0">
-        <adaptation xsi:type="hintco:XorPortAdaptation">
-          <children xsi:type="hintco:ExtrapolationAdaptation" implies="//@candidates.0/@cosimunits.2/@ports.0/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.1/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.2/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.3/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@adaptation" order="1"/>
-          <children xsi:type="hintco:InterpolationAdaptation" weight="10" implies="//@candidates.0/@cosimunits.2/@ports.0/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.1/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.2/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.3/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@adaptation" order="1"/>
-        </adaptation>
-      </ports>
-      <ports xsi:type="hintco:InputPortInstance" identifier="V_IN@expseu_" valueFrom="//@candidates.0/@cosimunits.3/@ports.2">
-        <adaptation xsi:type="hintco:XorPortAdaptation">
-          <children xsi:type="hintco:ExtrapolationAdaptation" implies="//@candidates.0/@cosimunits.2/@ports.0/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.1/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.2/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.3/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@adaptation" order="1"/>
-          <children xsi:type="hintco:InterpolationAdaptation" weight="10" implies="//@candidates.0/@cosimunits.2/@ports.0/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.1/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.2/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.3/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@adaptation" order="1"/>
-        </adaptation>
-      </ports>
-      <ports xsi:type="hintco:InputPortInstance" identifier="X_IN@expseu_" valueFrom="//@candidates.0/@cosimunits.3/@ports.3">
-        <adaptation xsi:type="hintco:XorPortAdaptation">
-          <children xsi:type="hintco:ExtrapolationAdaptation" implies="//@candidates.0/@cosimunits.2/@ports.0/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.1/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.2/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@ports.3/@adaptation/@children.0 //@candidates.0/@cosimunits.2/@adaptation" order="1"/>
-          <children xsi:type="hintco:InterpolationAdaptation" weight="10" implies="//@candidates.0/@cosimunits.2/@ports.0/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.1/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.2/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@ports.3/@adaptation/@children.1 //@candidates.0/@cosimunits.2/@adaptation" order="1"/>
-        </adaptation>
-      </ports>
-      <ports xsi:type="hintco:OutputPortInstance" identifier="ang_vel_out_rpm@expseu_" valueTo="//@candidates.0/@cosimunits.1/@ports.0"/>
-      <ports xsi:type="hintco:OutputPortInstance" identifier="F_OUT@expseu_" valueTo="//@candidates.0/@cosimunits.3/@ports.0"/>
-    </cosimunits>
-    <cosimunits xsi:type="hintco:CosimUnitInstance" identifier="LoadNSensor_FixedEuler_1Em6" declaration="//@csuDeclarations.3">
-      <adaptation xsi:type="hintco:MultiRateAdaptation" rate="10"/>
-      <ports xsi:type="hintco:InputPortInstance" identifier="F_INPUT@expseu_" valueFrom="//@candidates.0/@cosimunits.2/@ports.5">
-        <adaptation xsi:type="hintco:XorPortAdaptation">
-          <children xsi:type="hintco:ExtrapolationAdaptation" implies="//@candidates.0/@cosimunits.3/@ports.0/@adaptation/@children.0 //@candidates.0/@cosimunits.3/@adaptation" order="1"/>
-          <children xsi:type="hintco:InterpolationAdaptation" weight="10" implies="//@candidates.0/@cosimunits.3/@ports.0/@adaptation/@children.1 //@candidates.0/@cosimunits.3/@adaptation" order="1"/>
-        </adaptation>
-      </ports>
-      <ports xsi:type="hintco:OutputPortInstance" identifier="X_AFTER_LOAD@expseu_" valueTo="//@candidates.0/@cosimunits.1/@ports.1"/>
-      <ports xsi:type="hintco:OutputPortInstance" identifier="V_OUTPUT@expseu_" valueTo="//@candidates.0/@cosimunits.2/@ports.2"/>
-      <ports xsi:type="hintco:OutputPortInstance" identifier="X_OUTPUT@expseu_" valueTo="//@candidates.0/@cosimunits.2/@ports.3"/>
-    </cosimunits>
-  </candidates>
-  <csuDeclarations identifier="Scenario"/>
-  <csuDeclarations identifier="DLoopController_FixedEuler_1Em6"/>
-  <csuDeclarations identifier="EMAPlantNoLoad_FixedEuler_1Em6"/>
-  <csuDeclarations identifier="LoadNSensor_FixedEuler_1Em6"/>
-</hintco:HintConfiguration>

+ 9 - 3
HintCOEngine/src/ua/ansymo/hintco/AlgebraicLoopVariantProcessor.xtend

@@ -2,11 +2,12 @@ package ua.ansymo.hintco
 
 import java.util.Deque
 import org.eclipse.core.runtime.Assert
+import org.jgrapht.alg.connectivity.GabowStrongConnectivityInspector
 import org.jgrapht.alg.interfaces.AStarAdmissibleHeuristic
 import org.jgrapht.alg.shortestpath.AStarShortestPath
+import org.jgrapht.graph.DefaultWeightedEdge
 import org.jgrapht.graph.DirectedMultigraph
 import org.jgrapht.util.SupplierUtil
-import org.jgrapht.graph.DefaultWeightedEdge
 
 class AlgebraicLoopVariantProcessor implements IVariantProcessor {
 	
@@ -63,7 +64,7 @@ class AlgebraicLoopVariantProcessor implements IVariantProcessor {
 	def findOptimalTriggerSequenceStrongComponent(DirectedMultigraph<CosimUnitInstance, DefaultWeightedEdge> sc) {
 		val searchGraph = new LazySearchTree(sc)
 		val alg = new AStarShortestPath(searchGraph, new BasicHeuristic())
-		alg.getPath(searchGraph.root, searchGraph.bottom)
+		new Pair(searchGraph, alg.getPath(searchGraph.root, searchGraph.bottom))
 	}
 	
 	/*
@@ -74,7 +75,12 @@ class AlgebraicLoopVariantProcessor implements IVariantProcessor {
 	 * 3. Join the trigger sequences found for each component, and return
 	 */
 	def findOptimalTriggerSequence(DirectedMultigraph<CosimUnitInstance, DefaultWeightedEdge> graph) {
-		// TODO
+		val strongComponents = new GabowStrongConnectivityInspector(graph).stronglyConnectedSets()
+		Assert.isTrue(strongComponents.size==1, "Non strongly connected scenarios are not yet supported.")
+		// TODO: when multiple strong components exists, take each strong component and build a graph of it.
+		// Then put it through findOptimalTriggerSequenceStrongComponent
+		val searchGraphAndTriggerSeq = findOptimalTriggerSequenceStrongComponent(graph)
+		searchGraphAndTriggerSeq
 	}
 	
 	/*

+ 29 - 22
HintCOEngine/src/ua/ansymo/hintco/LazySearchTree.xtend

@@ -29,13 +29,18 @@ class LazySearchTree extends SimpleDirectedWeightedGraph<LazySearchNode, Default
 	
 	/*
 	 * Computes the edges and vertices that are outgoing from vertex, and adds them to the graph.
-	 * It considers three kinds of LazySearchNode: the EmptyTriggerSequence, the PartialTriggerSequence, and the CompleteTriggerSequence.
+	 * It considers three kinds of LazySearchNode: 
+	 * 	the EmptyTriggerSequence, the PartialTriggerSequence, and the CompleteTriggerSequence.
 	 * In the case of EmptyTriggerSequence, then the successors are all possible vertices on the graph.
-	 * In the case of PartialTriggerSequence, then the successors are all possible vertices on the graph, except for those that are already part of the trigger sequence (this is naive, see below).
+	 * In the case of PartialTriggerSequence, then the successors are all possible vertices on the graph, 
+	 * 		except for those that are already part of the trigger sequence (this is naive, see below).
 	 * In the case of CompleteTriggerSequence, then there are no successors, and an exception should be thrown.
-	 * I think it is possible to reduce the successors in the case of a PartialTriggerSequence. Essentially, one would expect the optimal trigger sequence to be a path in the triggerGraph. 
-	 * This is because whenever we pick a cosim unit A, any cosim units that depend on A have their cost reduced, so it would make sense that the optimal choice will be among them. 
-	 * However, for cosim scenarios that results in non strongly connected graphs, one may be forced to chose a successor which is not a successor of A in triggerGraph.
+	 * I think it is possible to reduce the successors in the case of a PartialTriggerSequence. 
+	 * 	Essentially, one would expect the optimal trigger sequence to be a path in the triggerGraph. 
+	 * This is because whenever we pick a cosim unit A, any cosim units that depend on A have their cost reduced, 
+	 * 	so it would make sense that the optimal choice will be among them. 
+	 * However, for cosim scenarios that results in non strongly connected graphs, 
+	 * 	one may be forced to chose a successor which is not a successor of A in triggerGraph.
 	 * Therefore I'm going for the most naive approach to just enum all, and let A* select the best choice.
 	 */
 	override outgoingEdgesOf(LazySearchNode vertex) {
@@ -47,7 +52,7 @@ class LazySearchTree extends SimpleDirectedWeightedGraph<LazySearchNode, Default
 		} else if (vertex instanceof CompleteTriggerSequence) {
 			throw new IllegalArgumentException()
 		} else if (vertex instanceof PartialTriggerSequence) {
-			val potentialSuccessors = triggerGraph.vertexSet.filter[v | !recIsAncestorOf(v, vertex)]
+			val potentialSuccessors = triggerGraph.vertexSet.filter[v | v!==vertex.unit && !recIsAncestorOf(v, vertex)]
 			if (potentialSuccessors.empty) {
 				val edge = this.addEdge(vertex, bottom)
 				Assert.isNotNull(edge) // Otherwise, this vertex has been expanded before, the solution found, and then the search problem got back here.
@@ -64,28 +69,29 @@ class LazySearchTree extends SimpleDirectedWeightedGraph<LazySearchNode, Default
 	
 	private def createNewEdge(LazySearchNode vertex, CosimUnitInstance v) {
 		val newV = new PartialTriggerSequence(v)
+		this.addVertex(newV)
 		val newEdge = this.addEdge(vertex, newV)
-		this.setEdgeWeight(newEdge, costOfEdge(newV))
+		Assert.isNotNull(newEdge)
+		val cost = costOfEdge(newV)
+		Assert.isNotNull(cost)
+		this.setEdgeWeight(newEdge, cost)
 	}
 	
 	def boolean recIsAncestorOf(CosimUnitInstance ancestorUnit, PartialTriggerSequence vertex) {
-		// Base case
 		val predecessors = vertex.incomingEdgesOf
-		Assert.isTrue(!predecessors.empty)
-		if (predecessors.size == 1) {
-			Assert.isTrue(predecessors.head.edgeSource instanceof EmptyTriggerSequence)
+		Assert.isTrue(predecessors.size == 1)
+		
+		// Base case
+		if (predecessors.head.edgeSource instanceof EmptyTriggerSequence) {
 			return false
 		}
-		for (p : predecessors) {
-			val partialP = p.edgeSource as PartialTriggerSequence
-			if (partialP.unit == ancestorUnit) {
-				return true
-			}
-			if (recIsAncestorOf(ancestorUnit, partialP)){
-				return true
-			}
+		
+		val partialP = predecessors.head.edgeSource as PartialTriggerSequence
+		if (partialP.unit == ancestorUnit) {
+			return true
 		}
-		return false
+		
+		return recIsAncestorOf(ancestorUnit, partialP)
 	}
 	
 	/*
@@ -94,8 +100,9 @@ class LazySearchTree extends SimpleDirectedWeightedGraph<LazySearchNode, Default
 	 */
 	def costOfEdge(PartialTriggerSequence B){
 		val Bunit = B.unit
-		val incommingEdges = triggerGraph.incomingEdgesOf(Bunit).filter[inc | recIsAncestorOf(triggerGraph.getEdgeSource(inc), B)]
-		val cost = incommingEdges.map[edge|triggerGraph.getEdgeWeight(edge)].reduce[p1, p2|p1+p2]
+		val incommingEdges = triggerGraph.incomingEdgesOf(Bunit).filter[inc | !recIsAncestorOf(triggerGraph.getEdgeSource(inc), B)]
+		val costOrNull = incommingEdges.map[edge|triggerGraph.getEdgeWeight(edge)].reduce[p1, p2|p1+p2]
+		val cost = if (costOrNull === null) 0.0 else costOrNull
 		return cost
 	}
 }

+ 80 - 42
HintCOEngine/test/ua/ansymo/hintco/test/AlgebraicLoopProcessingTests.xtend

@@ -9,11 +9,16 @@ import ua.ansymo.hintco.ModelQuery
 import ua.ansymo.hintco.ModelStorage
 
 import static org.junit.Assert.*
+import org.jgrapht.alg.connectivity.GabowStrongConnectivityInspector
+import ua.ansymo.hintco.RootCandidateScenario
+import org.jgrapht.graph.DefaultWeightedEdge
+import ua.ansymo.hintco.EmptyTriggerSequence
+import ua.ansymo.hintco.CompleteTriggerSequence
+import ua.ansymo.hintco.PartialTriggerSequence
 
 class AlgebraicLoopProcessingTests {
 	
-	@Test
-	def void addConstraintsWithAlgebraicLoopsTest() {
+	def loadAndRunAlgLoopsSample((RootCandidateScenario)=>void testBody){
 		val loader = new ModelStorage()
 		
 		val src = loader.loadCandidates("instances/algebraic_loop_opt_test.hintco")
@@ -23,53 +28,86 @@ class AlgebraicLoopProcessingTests {
 			],
 			[ns, vId, constraints, cs |
 				val scenario = ModelQuery.findRootScenario(ns)
-				val cosimUnits = scenario.cosimunits.filter(CosimUnitInstance)
-				val A = cosimUnits.findFirst[u | u.identifier=="A"]
-				val B = cosimUnits.findFirst[u | u.identifier=="B"]
-				assertTrue(A.inputPorts.findFirst[p|p.identifier=="3fromB"].precedes.contains(A))
-				assertTrue(A.inputPorts.findFirst[p|p.identifier=="5fromC"].precedes.contains(A))
-				assertFalse(B.inputPorts.findFirst[p|p.identifier=="10fromC"].precedes.contains(B))
+				testBody.apply(scenario)
 			])
 		generator.createVariantTree(src)
 		generator.generateVariants(src, 1, true)
 	}
 	
+	@Test
+	def void addConstraintsWithAlgebraicLoopsTest() {
+		loadAndRunAlgLoopsSample([ scenario |
+			val cosimUnits = scenario.cosimunits.filter(CosimUnitInstance)
+			val A = cosimUnits.findFirst[u|u.identifier == "A"]
+			val B = cosimUnits.findFirst[u|u.identifier == "B"]
+			assertTrue(A.inputPorts.findFirst[p|p.identifier == "3fromB"].precedes.contains(A))
+			assertTrue(A.inputPorts.findFirst[p|p.identifier == "5fromC"].precedes.contains(A))
+			assertFalse(B.inputPorts.findFirst[p|p.identifier == "10fromC"].precedes.contains(B))
+		])
+	}
+	
 	@Test
 	def void transformToCosimUnitGraphTest(){
-		val loader = new ModelStorage()
-		
-		val src = loader.loadCandidates("instances/algebraic_loop_opt_test.hintco")
-		
-		val generator = new CandidatesGenerator(new ConstraintChecker(), [n, alternatives, ns |
-				true
-			],
-			[ns, vId, constraints, cs |
-				val scenario = ModelQuery.findRootScenario(ns)
-				
-				val graph = new AlgebraicLoopVariantProcessor(null).transformToCosimUnitGraph(scenario)
-				
-				assertNotNull(graph)
-		
-				val vertices = graph.vertexSet
-				assertEquals(3, vertices.size)
-				val A = vertices.findFirst[v | v.identifier == "A"]
-				val B = vertices.findFirst[v | v.identifier == "B"]
-				val C = vertices.findFirst[v | v.identifier == "C"]
-				
-				val A4B = graph.getAllEdges(A, B).head
-				val B3A = graph.getAllEdges(B, A).head
-				val B3C = graph.getAllEdges(B, C).head
-				
-				assertNotNull(A4B)
-				assertNotNull(B3A)
-				assertNotNull(B3C)
-				assertTrue(graph.getAllEdges(C, B).isEmpty)
-				assertEquals(4.0,graph.getEdgeWeight(A4B),1e-4)
-				assertEquals(3.0,graph.getEdgeWeight(B3A),1e-4)
-				assertEquals(3.0,graph.getEdgeWeight(B3C),1e-4)
-			])
-		generator.createVariantTree(src)
-		generator.generateVariants(src, 1, true)
+		loadAndRunAlgLoopsSample([ scenario |
+			val graph = new AlgebraicLoopVariantProcessor(null).transformToCosimUnitGraph(scenario)
+
+			assertNotNull(graph)
+
+			val vertices = graph.vertexSet
+			assertEquals(3, vertices.size)
+			val A = vertices.findFirst[v|v.identifier == "A"]
+			val B = vertices.findFirst[v|v.identifier == "B"]
+			val C = vertices.findFirst[v|v.identifier == "C"]
+
+			val A4B = graph.getAllEdges(A, B).head
+			val B3A = graph.getAllEdges(B, A).head
+			val B3C = graph.getAllEdges(B, C).head
+
+			assertNotNull(A4B)
+			assertNotNull(B3A)
+			assertNotNull(B3C)
+			assertTrue(graph.getAllEdges(C, B).isEmpty)
+			assertEquals(4.0, graph.getEdgeWeight(A4B), 1e-4)
+			assertEquals(3.0, graph.getEdgeWeight(B3A), 1e-4)
+			assertEquals(3.0, graph.getEdgeWeight(B3C), 1e-4)
+		])
+	}
+	
+	@Test
+	def void strongComponentsTest(){
+		loadAndRunAlgLoopsSample([ scenario |
+			val graph = new AlgebraicLoopVariantProcessor(null).transformToCosimUnitGraph(scenario)
+			
+			val strongComponents = new GabowStrongConnectivityInspector(graph).stronglyConnectedSets()
+			assertEquals(1, strongComponents.size)
+			assertTrue(strongComponents.head.exists[v | v.identifier == "A"])
+			assertTrue(strongComponents.head.exists[v | v.identifier == "B"])
+			assertTrue(strongComponents.head.exists[v | v.identifier == "C"])
+		])
+	}
+	
+	@Test
+	def void optimalTriggerSequenceTest(){
+		loadAndRunAlgLoopsSample([ scenario |
+			val algLoopProcessor = new AlgebraicLoopVariantProcessor(null)
+			val graph = algLoopProcessor.transformToCosimUnitGraph(scenario)
+			val searchGraphAndTriggerSeq = algLoopProcessor.findOptimalTriggerSequence(graph)
+			val searchGraph = searchGraphAndTriggerSeq.key
+			val triggerSeq = searchGraphAndTriggerSeq.value
+			
+			val getTrg = [int idx | (searchGraph.getEdgeTarget(triggerSeq.edgeList.get(idx)) as PartialTriggerSequence)]
+			val getWeight = [int idx | (searchGraph.getEdgeWeight(triggerSeq.edgeList.get(idx)))]
+			
+			assertEquals(5, triggerSeq.vertexList.length)
+			assertTrue(searchGraph.getEdgeSource(triggerSeq.edgeList.head) instanceof EmptyTriggerSequence)
+			assertTrue(searchGraph.getEdgeTarget(triggerSeq.edgeList.last) instanceof CompleteTriggerSequence)
+			assertEquals("B", getTrg.apply(0).unit.identifier)
+			assertEquals(4.0, getWeight.apply(0), 1e-4)
+			assertEquals("C", getTrg.apply(1).unit.identifier)
+			assertEquals(3.0, getWeight.apply(1), 1e-4)
+			assertEquals("A", getTrg.apply(2).unit.identifier)
+			assertEquals(0.0, getWeight.apply(2), 1e-4)
+		])
 	}
 	
 }