瀏覽代碼

result of addConstraint can have three possible values

Claudio Gomes 5 年之前
父節點
當前提交
b2488a7418
共有 1 個文件被更改,包括 284 次插入253 次删除
  1. 284 253
      HintCOEngine/src/ua/ansymo/hintco/CandidatesGenerator.xtend

+ 284 - 253
HintCOEngine/src/ua/ansymo/hintco/CandidatesGenerator.xtend

@@ -10,19 +10,19 @@ import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 
 class CandidatesGenerator {
-	
+
 	IConstraintChecker constraintChecker
 	IVariantValidator variantValidator
 	IVariantProcessor processor
-	
+
 	Logger logger = LoggerFactory.getLogger(typeof(CandidatesGenerator))
-	
-	new (IConstraintChecker c, IVariantValidator v, IVariantProcessor p){
+
+	new(IConstraintChecker c, IVariantValidator v, IVariantProcessor p) {
 		constraintChecker = c
 		variantValidator = v
 		processor = p
 	}
-	
+
 	/*
 	 * Recursively goes through every path on the variant diagram and creates the necessary precedence constraints, cleaning up after the function call (that's why we use a stack to keep groups of constraints together).
 	 * Optimization: when optimizeAlgebraicLoops==false, check if there is a cycle every time there are a new edge is created. Then abort that function call, as any children of the variant tree will not be valid. Possible paper that addresses this: https://dl.acm.org/citation.cfm?id=1033207
@@ -32,399 +32,432 @@ class CandidatesGenerator {
 	 * 
 	 * When optimizeAlgebraicLoops==true, we have to continue the recursion, and add constraints as needed. When a variant is fully specified, we launch the algebraic loop optimizer in order to find a schedule for the cosim scenario.
 	 */
-	def generateVariants(HintConfiguration cs, int maxVariants, boolean optimizeAlgebraicLoops){
-		
+	def generateVariants(HintConfiguration cs, int maxVariants, boolean optimizeAlgebraicLoops) {
+
 		// Assumes that there is a variant diagram created.
 		Assert.isTrue(cs.root !== null)
-		
+
 		if (optimizeAlgebraicLoops && maxVariants > 1) {
-			logger.warn("Multiple variants chosen with algebraic loop optimizer enabled. This will cause the optimizer to be used for every variant, and might be slow.")
+			logger.warn(
+				"Multiple variants chosen with algebraic loop optimizer enabled. This will cause the optimizer to be used for every variant, and might be slow.")
 		}
-		
-		val genVariants = recGenerateVariants(newLinkedList(cs.root), cs, new ConstraintsStack(), maxVariants, optimizeAlgebraicLoops)
+
+		val genVariants = recGenerateVariants(newLinkedList(cs.root), cs, new ConstraintsStack(), maxVariants,
+			optimizeAlgebraicLoops)
 		return genVariants
 	}
-	
-	def processAdaptations(HintConfiguration cs){
+
+	def processAdaptations(HintConfiguration cs) {
 		// Completes adaptations such as the power bond adaptation.
-		
 		val f = HintcoFactory.eINSTANCE
-		
+
 		// For each power adaptation, creates an input port and an output power.
-		
 		val adaptations = cs.eAllContents.filter(PowerBondAdaptation).toList
-		
-		for (pbAdaptation : adaptations){
-			Assert.isTrue(pbAdaptation.PIn === null )
+
+		for (pbAdaptation : adaptations) {
+			Assert.isTrue(pbAdaptation.PIn === null)
 			Assert.isTrue(pbAdaptation.POut === null)
 			val adaptedUnit = pbAdaptation.adapted as CosimUnitInstance
-			
+
 			// Create powerOut declaration
 			{
 				val pOutId = pbAdaptation.effort.identifier + "_" + pbAdaptation.flow.identifier + "_pOut"
-				Assert.isTrue(!adaptedUnit.ports.exists[p | p.identifier == pOutId])
-				
+				Assert.isTrue(!adaptedUnit.ports.exists[p|p.identifier == pOutId])
+
 				val pOutPort = f.createOutputPortInstance
-				pOutPort.identifier = pOutId	
+				pOutPort.identifier = pOutId
 				adaptedUnit.ports.add(pOutPort)
 				pbAdaptation.POut = pOutPort
 			}
-			
+
 			// Create powerIn declaration
 			{
 				val pInId = pbAdaptation.effort.identifier + "_" + pbAdaptation.flow.identifier + "_pIn"
-				Assert.isTrue(!adaptedUnit.ports.exists[p | p.identifier == pInId])
-				
+				Assert.isTrue(!adaptedUnit.ports.exists[p|p.identifier == pInId])
+
 				val pInPort = f.createInputPortInstance
-				pInPort.identifier = pInId	
+				pInPort.identifier = pInId
 				adaptedUnit.ports.add(pInPort)
 				pbAdaptation.PIn = pInPort
 			}
 		}
-		
+
 		// Connect the power out with the power Ins
-		for (pbAdaptation : adaptations){
-			if (pbAdaptation.POut.valueTo.empty){
+		for (pbAdaptation : adaptations) {
+			if (pbAdaptation.POut.valueTo.empty) {
 				pbAdaptation.POut.valueTo.add(pbAdaptation.dual.PIn)
 			}
 		}
-		
+
 	}
-	
-	def createVariantTree(HintConfiguration cs){
+
+	def createVariantTree(HintConfiguration cs) {
 		// Creates a variant tree.
 		// The first level of the tree is the xor with the strongest weights.
-		
 		// Clear existing information
 		cs.root = null
 		cs.nodes.clear()
-		
+
 		// Get factory for creating objects.
 		val f = HintcoFactory.eINSTANCE
-		
+
 		val id = 0 // Generates unique ids for VariantTree objects. Allows traceability between results and variants.
-		
 		// Created sorted alternatives: highest weight first
 		val sortedAlts = newArrayList()
-		cs.eAllContents.filter(Alternative).forEach[a |
+		cs.eAllContents.filter(Alternative).forEach [ a |
 			// creates. The boolean is used later to mark alternatives that have been considered.
 			sortedAlts.add(new MarkedAlternative(a))
 		]
-		sortedAlts.sortInplace([a,b | b.alternative.weight - a.alternative.weight])
-		
+		sortedAlts.sortInplace([a, b|b.alternative.weight - a.alternative.weight])
+
 		// recursively create variant tree, starting with the highest weight.
 		val rootNode = f.createVariantDiagram
-		rootNode.identifier = ""+id
+		rootNode.identifier = "" + id
 		cs.root = rootNode
 		cs.nodes.add(rootNode)
-		recCreateVariantTree(#[rootNode], sortedAlts, 0, cs, id+1)
+		recCreateVariantTree(#[rootNode], sortedAlts, 0, cs, id + 1)
 		println()
 	}
-	
-	def private int recGenerateVariants(Deque<VariantDiagram> ns, 
-										HintConfiguration cs, 
-										ConstraintsStack constraints, int maxVariants, boolean optimizeAlgebraicLoops) {
+
+	def private int recGenerateVariants(Deque<VariantDiagram> ns, HintConfiguration cs, ConstraintsStack constraints,
+		int maxVariants, boolean optimizeAlgebraicLoops) {
 		// Store size of stack for assertion
 		val callDepth = constraints.size
-		
+
 		var numGenVariants = 0
-		
+
 		// Create new constraint map and put it at the head of the queue.
 		val HashMap<PrecendenceNode, Set<PrecendenceNode>> localConstraints = newHashMap()
 		constraints.push(localConstraints)
-		
+
 		val n = ns.peek() // Head of the queue is the current element being visited.
 		
-		try {
-			addConstraintsBasedOnAlternative(n.alternative, constraints, n, cs)
-			
-			// Check invariant on localConstraints
-			localConstraints.forEach[k, v|
-				Assert.isTrue(k.precedes.containsAll(v))
-			]
-			
-			if (n.children.empty){
+		val introducedCycle = addConstraintsBasedOnAlternative(n.alternative, constraints, n, cs)
+
+		// Check invariant on localConstraints
+		localConstraints.forEach [ k, v |
+			Assert.isTrue(k.precedes.containsAll(v))
+		]
+
+		if (!introducedCycle || optimizeAlgebraicLoops) {
+			if (n.children.empty) {
 				// Base case
-				if (numGenVariants < maxVariants){
+				if (numGenVariants < maxVariants) {
 					// Propagate precedence constraints from inner scenarios to their parents.
 					// If two nodes are indirectly (via a path in the children nodes) connected, then they should be connected directly.
 					// This will ensure that the topological sort can safely ignore child nodes.
 					propagateConstraints(n, ns, constraints, cs)
-					
-					val variantID = ns.map[n1 | n1.identifier].reduce[n1, n2| n2 + "_" + n1]
+
+					val variantID = ns.map[n1|n1.identifier].reduce[n1, n2|n2 + "_" + n1]
 					// mark alternatives as selected, so the processor knows which alternatives have been selected.
 					// Exclude root because it has no alternative
 					markSelected(ns, true)
 					processor.process(ns, variantID, constraints, cs)
 					markSelected(ns, false)
-					numGenVariants++				
+					numGenVariants++
 				}
 			} else {
 				// Recurse
 				for (c : n.children) {
-					if (numGenVariants < maxVariants && variantValidator.validVariant(c, n.children.filter[a | a!==c], ns)){
+					if (numGenVariants < maxVariants &&
+						variantValidator.validVariant(c, n.children.filter[a|a !== c], ns)) {
 						ns.push(c)
-						val childGenVariants = recGenerateVariants(ns, cs, constraints, maxVariants-numGenVariants, optimizeAlgebraicLoops)
+						val childGenVariants = recGenerateVariants(ns, cs, constraints, maxVariants - numGenVariants,
+							optimizeAlgebraicLoops)
 						numGenVariants = numGenVariants + childGenVariants
-						ns.pop()	
+						ns.pop()
 					}
 				}
 			}
-		} catch (InfeasibleConstraintException e) {
-			// Do nothing.
-			logger.debug("The last added constraint is infeasible. Trying other alternatives.")
+		} else {
+			logger.debug("Constraints form dependency loop and the loop optimizer is disabled. Exploring other alternatives.")
 		}
-		
+
 		// Revert changes in model
-		localConstraints.forEach[k, v| 
+		localConstraints.forEach [ k, v |
 			Assert.isTrue(k.precedes.removeAll(v))
 		]
 		constraints.pop()
-		
+
 		Assert.isTrue(callDepth == constraints.size)
 		return numGenVariants
 	}
-	
-	def addConstraintsBasedOnAlternative(Alternative alternative, ConstraintsStack constraints, VariantDiagram n, HintConfiguration cs) {
+
+	def addConstraintsBasedOnAlternative(Alternative alternative, ConstraintsStack constraints, VariantDiagram n,
+		HintConfiguration cs) {
+		var introducedCycle = false
 		switch alternative {
-				RootCandidateScenario: {
-					/*
-					 * Recursively create I/O and DoStep constraints.
-					 */
-					val scenario = (alternative as RootCandidateScenario)
-					logger.debug("Processing Alternative RootCandidateScenario {}", scenario.identifier)
-					
-					for (outP : scenario.eAllContents.filter(OutputPortInstance).toIterable) {
-						// Create constraints Outports/HierarchicalPorts -> InPorts/HierarchicalPorts
-						for (inP : outP.valueTo) {
-							logger.debug("Attempting to add constraint {}.{}->{}.{}.", outP.unit.identifier, outP.identifier, inP.unit.identifier, inP.identifier)
-							val success = addConstraint(outP, inP, constraints, n, cs)
-							logger.debug("Success: {}", success)
-						}
-						
-						//Create feedthrough constraints:
-						for (inP : outP.internalValueFrom) {
-							logger.debug("Attempting to add constraint {}.{}->{}.{}.", inP.unit.identifier, inP.identifier, outP.unit.identifier, outP.identifier)
-							val success = addConstraint(inP, outP, constraints, n, cs)
-							logger.debug("Success: {}", success)
+			RootCandidateScenario: {
+				/*
+				 * Recursively create I/O and DoStep constraints.
+				 */
+				val scenario = (alternative as RootCandidateScenario)
+				logger.debug("Processing Alternative RootCandidateScenario {}", scenario.identifier)
+
+				for (outP : scenario.eAllContents.filter(OutputPortInstance).toIterable) {
+					// Create constraints Outports/HierarchicalPorts -> InPorts/HierarchicalPorts
+					for (inP : outP.valueTo) {
+						logger.debug("Adding constraint {}.{}->{}.{}.", outP.unit.identifier, outP.identifier,
+							inP.unit.identifier, inP.identifier)
+						val success = addConstraint(outP, inP, constraints, n, cs)
+						logger.debug("Success: {}", success)
+						if (success == AddConstraintResult.CYCLE) {
+							introducedCycle = true
 						}
 					}
-					
-					// Create constraints between outputs and doStep.
-					// We assume that at the beginning of the cosim step, every input/output is defined. 
-					// Therefore, doStep must be done before getting any output.
-					for (outP : scenario.eAllContents.filter(OutputPortInstance)
-											.filter[! it.isInput] // ip can be a hierarchical port, 
-											.toIterable // therefore we need to make sure it is connected to an internal unit. 
-											//Otherwise it is not an input port.
-					) {
-						logger.debug("Attempting to add constraint {}->{}.{}.", outP.unit.identifier, outP.unit.identifier, outP.identifier)
-						val success = addConstraint(outP.unit, outP, constraints, n, cs)
+
+					// Create feedthrough constraints:
+					for (inP : outP.internalValueFrom) {
+						logger.debug("Attempting to add constraint {}.{}->{}.{}.", inP.unit.identifier, inP.identifier,
+							outP.unit.identifier, outP.identifier)
+						val success = addConstraint(inP, outP, constraints, n, cs)
 						logger.debug("Success: {}", success)
-					}
-					
-					// The DoStep of each child unit must conclude before the doStep of the parent unit concludes
-					// Furthermore, every output operation of a child unit must come before every getout operation of the parent unit.
-					// This, together with the previous set of constraints, ensures that each hierarchical unit's doStep operation is atomic.
-					for (parentUnit : scenario.eAllContents.filter(HierarchicalCosimUnit).toIterable){
-						val parentOutputPorts = parentUnit.outputPorts
-						for (childUnit : parentUnit.cosimunits){
-							logger.debug("Attempting to add constraint {}->{}.", childUnit.identifier, parentUnit.identifier)
-							val success = addConstraint(childUnit, parentUnit, constraints, n, cs)
-							logger.debug("Success: {}", success)
-							
-							for (childOutP : childUnit.outputPorts){
-								for (parentOutP : parentOutputPorts){
-									// TODO: Use toString method for printing, and move the logging operations to inside a method.
-									logger.debug("Attempting to add constraint {}.{}->{}.{}.", childOutP.unit.identifier, childOutP.identifier, parentOutP.unit.identifier, parentOutP.identifier)
-									val succ = addConstraint(childOutP, parentOutP, constraints, n, cs)
-									logger.debug("Success: {}", succ)
-								}
-							}
+						if (success == AddConstraintResult.CYCLE) {
+							introducedCycle = true
 						}
 					}
 				}
-				ExtrapolationAdaptation: {
-					// In case of an extrapolation on an input, the doStep must be done before setting the input,
-					// because whenever the input is set, it is being set for timestamp t+H.
-
-					val adaptation = alternative as ExtrapolationAdaptation
-					val adapted = adaptation.adapted()
-					logger.debug("Processing Alternative ExtrapolationAdaptation of {}", adapted.identifier)
-					Assert.isNotNull(adapted)
-					Assert.isTrue(adapted instanceof InputPortInstance)
-					val adaptedPort = adapted as InputPortInstance
-					Assert.isTrue(adaptedPort.unit instanceof CosimUnitInstance)
-					
-					logger.debug("Attempting to add constraint {}->{}.{}.", adaptedPort.unit.identifier, adaptedPort.unit.identifier, adaptedPort.identifier)
-					val success = addConstraint(adaptedPort.unit, adaptedPort, constraints, n, cs)
-					logger.debug("Success: {}", success)
-					
-				}
-				InterpolationAdaptation: {
-					// In case of an interpolation on an input, the input must be set before the doStep,
-					// because whenever the input is set, it is being set for timestamp t+H.
-					
-					val adaptation = alternative as InterpolationAdaptation
-					val adapted = adaptation.adapted()
-					logger.debug("Processing Alternative InterpolationAdaptation of {}", adapted.identifier)
-					Assert.isNotNull(adapted)
-					Assert.isTrue(adapted instanceof InputPortInstance)
-					val adaptedPort = adapted as InputPortInstance
-					
-					logger.debug("Attempting to add constraint {}.{}->{}.{}.", adaptedPort.unit.identifier, adaptedPort.identifier, adaptedPort.unit.identifier)
-					val success = addConstraint(adaptedPort, adaptedPort.unit, constraints, n, cs)
+
+				// Create constraints between outputs and doStep.
+				// We assume that at the beginning of the cosim step, every input/output is defined. 
+				// Therefore, doStep must be done before getting any output.
+				for (outP : scenario.eAllContents.filter(OutputPortInstance).filter[! it.isInput] // ip can be a hierarchical port, 
+				.toIterable // therefore we need to make sure it is connected to an internal unit. 
+				// Otherwise it is not an input port.
+				) {
+					logger.debug("Adding constraint {}->{}.{}.", outP.unit.identifier, outP.unit.identifier,
+						outP.identifier)
+					val success = addConstraint(outP.unit, outP, constraints, n, cs)
 					logger.debug("Success: {}", success)
-					
-				}
-				DecompositionPortAdaptation: {
-					// Nothing to do.
-				}
-				DecompositionUnitAdaptation: {
-					// Nothing to do.
+					if (success == AddConstraintResult.CYCLE) {
+						introducedCycle = true
+					}
 				}
-				PowerBondAdaptation: {
-					// This needs to be rewritten, to accomodate the hierarchical cosim case 
-					//    and the assumption that at the begining of the cosim step, every port is defined.
-					throw new UnsupportedOperationException("Powerbond adaptations need to be re-implemented for hierarchical cosim.")
-					
-					/*
-					val adaptation = alternative as PowerBondAdaptation
-					val adapted = adaptation.adapted()
-					logger.debug("Processing Alternative PowerBondAdaptation of {}", adapted.identifier)
-					Assert.isNotNull(adapted)
-					Assert.isTrue(adapted instanceof UnitInstance)
-					val unit = adapted as UnitInstance
-					
-					Assert.isTrue(adaptation.POut.unit === unit)
-					
-					logger.debug("Attempting to add constraint {}.{}->{}.", unit.identifier, adaptation.POut.identifier, unit.identifier)
-					val success = addConstraint(adaptation.POut, unit, constraints, n, cs)
-					logger.debug("Success: {}", success)
-					 */
+
+				// The DoStep of each child unit must conclude before the doStep of the parent unit concludes
+				// Furthermore, every output operation of a child unit must come before every getout operation of the parent unit.
+				// This, together with the previous set of constraints, ensures that each hierarchical unit's doStep operation is atomic.
+				for (parentUnit : scenario.eAllContents.filter(HierarchicalCosimUnit).toIterable) {
+					val parentOutputPorts = parentUnit.outputPorts
+					for (childUnit : parentUnit.cosimunits) {
+						logger.debug("Adding constraint {}->{}.", childUnit.identifier, parentUnit.identifier)
+						val success = addConstraint(childUnit, parentUnit, constraints, n, cs)
+						logger.debug("Success: {}", success)
+						if (success == AddConstraintResult.CYCLE) {
+							introducedCycle = true
+						}
+
+						for (childOutP : childUnit.outputPorts) {
+							for (parentOutP : parentOutputPorts) {
+								// TODO: Use toString method for printing, and move the logging operations to inside a method.
+								logger.debug("Adding constraint {}.{}->{}.{}.", childOutP.unit.identifier,
+									childOutP.identifier, parentOutP.unit.identifier, parentOutP.identifier)
+								val succ = addConstraint(childOutP, parentOutP, constraints, n, cs)
+								logger.debug("Success: {}", succ)
+								if (success == AddConstraintResult.CYCLE) {
+									introducedCycle = true
+								}
+							}
+						}
+					}
 				}
-				MultiRateAdaptation: {
-					// Nothing to do
+			}
+			ExtrapolationAdaptation: {
+				// In case of an extrapolation on an input, the doStep must be done before setting the input,
+				// because whenever the input is set, it is being set for timestamp t+H.
+				val adaptation = alternative as ExtrapolationAdaptation
+				val adapted = adaptation.adapted()
+				logger.debug("Processing Alternative ExtrapolationAdaptation of {}", adapted.identifier)
+				Assert.isNotNull(adapted)
+				Assert.isTrue(adapted instanceof InputPortInstance)
+				val adaptedPort = adapted as InputPortInstance
+				Assert.isTrue(adaptedPort.unit instanceof CosimUnitInstance)
+
+				logger.debug("Adding constraint {}->{}.{}.", adaptedPort.unit.identifier, adaptedPort.unit.identifier,
+					adaptedPort.identifier)
+				val success = addConstraint(adaptedPort.unit, adaptedPort, constraints, n, cs)
+				logger.debug("Success: {}", success)
+
+			}
+			InterpolationAdaptation: {
+				// In case of an interpolation on an input, the input must be set before the doStep,
+				// because whenever the input is set, it is being set for timestamp t+H.
+				val adaptation = alternative as InterpolationAdaptation
+				val adapted = adaptation.adapted()
+				logger.debug("Processing Alternative InterpolationAdaptation of {}", adapted.identifier)
+				Assert.isNotNull(adapted)
+				Assert.isTrue(adapted instanceof InputPortInstance)
+				val adaptedPort = adapted as InputPortInstance
+
+				logger.debug("Adding constraint {}.{}->{}.{}.", adaptedPort.unit.identifier, adaptedPort.identifier,
+					adaptedPort.unit.identifier)
+				val success = addConstraint(adaptedPort, adaptedPort.unit, constraints, n, cs)
+				logger.debug("Success: {}", success)
+				if (success == AddConstraintResult.CYCLE) {
+					introducedCycle = true
 				}
-				default:
-					if (alternative !== null){
-						throw new UnsupportedOperationException()
-					}
+
+			}
+			DecompositionPortAdaptation: {
+				// Nothing to do.
 			}
+			DecompositionUnitAdaptation: {
+				// Nothing to do.
+			}
+			PowerBondAdaptation: {
+				// This needs to be rewritten, to accomodate the hierarchical cosim case 
+				// and the assumption that at the begining of the cosim step, every port is defined.
+				throw new UnsupportedOperationException(
+					"Powerbond adaptations need to be re-implemented for hierarchical cosim.")
+
+			/*
+			 * val adaptation = alternative as PowerBondAdaptation
+			 * val adapted = adaptation.adapted()
+			 * logger.debug("Processing Alternative PowerBondAdaptation of {}", adapted.identifier)
+			 * Assert.isNotNull(adapted)
+			 * Assert.isTrue(adapted instanceof UnitInstance)
+			 * val unit = adapted as UnitInstance
+			 * 
+			 * Assert.isTrue(adaptation.POut.unit === unit)
+			 * 
+			 * logger.debug("Attempting to add constraint {}.{}->{}.", unit.identifier, adaptation.POut.identifier, unit.identifier)
+			 * val success = addConstraint(adaptation.POut, unit, constraints, n, cs)
+			 * logger.debug("Success: {}", success)
+			 */
+			}
+			MultiRateAdaptation: {
+				// Nothing to do
+			}
+			default:
+				if (alternative !== null) {
+					throw new UnsupportedOperationException()
+				}
+		}
+		return introducedCycle
 	}
-	
-	def propagateConstraints(VariantDiagram n, Deque<VariantDiagram> ns, ConstraintsStack constraints, HintConfiguration cs) {
+
+	def propagateConstraints(VariantDiagram n, Deque<VariantDiagram> ns, ConstraintsStack constraints,
+		HintConfiguration cs) {
 		val rootScenario = ModelQuery.findRootScenario(ns)
 		val scenarios = rootScenario.eAllContents.filter(HierarchicalCosimUnit).toList
-		
-		for (scenario: scenarios){
+		for (scenario : scenarios) {
 			val List<PrecendenceNode> scenarioNodes = newLinkedList()
 			scenarioNodes.add(scenario as PrecendenceNode)
 			scenarioNodes.addAll(scenario.inputPorts)
 			scenarioNodes.addAll(scenario.outputPorts)
-			
-			for (scenarioNodeSrc : scenarioNodes){
-				for (scenarioNodeTrg : scenarioNodes){
-					if (! scenarioNodeSrc.precedes.contains(scenarioNodeTrg) && 
-							ModelQuery.pathExists(scenarioNodeSrc, scenarioNodeTrg, constraints, cs)
-					){
-						Assert.isTrue(addConstraint(scenarioNodeSrc, scenarioNodeTrg, constraints, n, cs))
+
+			for (scenarioNodeSrc : scenarioNodes) {
+				for (scenarioNodeTrg : scenarioNodes) {
+					if (! scenarioNodeSrc.precedes.contains(scenarioNodeTrg) &&
+						ModelQuery.pathExists(scenarioNodeSrc, scenarioNodeTrg, constraints, cs)) {
+						Assert.isTrue(
+							addConstraint(scenarioNodeSrc, scenarioNodeTrg, constraints, n, cs) ==
+								AddConstraintResult.SUCCESS)
 					}
 				}
 			}
 		}
 	}
-	
+
 	def markSelected(Deque<VariantDiagram> ns, boolean selected) {
-		for (n : ns){
-			if (n.alternative !== null){
+		for (n : ns) {
+			if (n.alternative !== null) {
 				n.alternative.selected = selected
 			}
 		}
 	}
-	
-	def private addConstraint(PrecendenceNode src, PrecendenceNode trg, 
-								ConstraintsStack constraints, 
-								VariantDiagram n,
-								HintConfiguration cs
-	){
+
+	enum AddConstraintResult {
+		SUCCESS,
+		EXISTS,
+		CYCLE
+	}
+
+	def private addConstraint(
+		PrecendenceNode src,
+		PrecendenceNode trg,
+		ConstraintsStack constraints,
+		VariantDiagram n,
+		HintConfiguration cs
+	) {
 		// check if constraint already exists.
-		if (!constraints.stackContains(src, trg) && 
-		  !src.precedes.contains(trg)) {
+		if (!constraints.stackContains(src, trg) && !src.precedes.contains(trg)) {
 			// Add constraint
-			if (constraintChecker.constraintIsFeasible(src, trg, n, constraints, cs)){ // avoid rec calls to children for variants that cannot be implemented.
+			if (constraintChecker.constraintIsFeasible(src, trg, n, constraints, cs)) { // avoid rec calls to children for variants that cannot be implemented.
 				constraints.safeAdd(src, trg)
 				src.precedes.add(trg)
-				return true
+				return AddConstraintResult.SUCCESS
 			} else {
-				throw new InfeasibleConstraintException() 
+				return AddConstraintResult.CYCLE
 			}
 		} else {
-			return false
+			return AddConstraintResult.EXISTS
 		}
 	}
-	
-	def private void recCreateVariantTree(List<VariantDiagram> roots, ArrayList<MarkedAlternative> sortedAlts, int highestAltIdx, HintConfiguration cs, int id) {
+
+	def private void recCreateVariantTree(List<VariantDiagram> roots, ArrayList<MarkedAlternative> sortedAlts,
+		int highestAltIdx, HintConfiguration cs, int id) {
 		// mark highest weight alternative from the list of alternatives
 		// and mark siblings as well.
 		// highestAltIdx stores where we can start searching
-		
 		val f = HintcoFactory.eINSTANCE
-		
+
 		// Base of the recursion:
 		if (highestAltIdx >= sortedAlts.length)
-			return 
-		
+			return
+
 		val highestAlt = sortedAlts.get(highestAltIdx)
 		highestAlt.inVariantTree = true
 		// find siblings. 
 		val siblings = collectMarkSiblings(highestAlt, sortedAlts, highestAltIdx)
-		
+
 		Assert.isTrue(siblings.length > 0)
-		
+
 		// Create a branch per alternative, under each of the roots
 		val newNodes = newArrayList
-		siblings.forEach[a,i |
+		siblings.forEach [ a, i |
 			val v = f.createVariantDiagram
-			v.identifier = ""+(id+i)
+			v.identifier = "" + (id + i)
 			v.alternative = a.alternative
 			newNodes.add(v)
 		]
-		
+
 		val pl = cs.nodes.length
-		if (! cs.nodes.addAll(newNodes) ) throw new RuntimeException()
+		if(! cs.nodes.addAll(newNodes)) throw new RuntimeException()
 		Assert.isTrue(cs.nodes.length == pl + newNodes.length)
-		
-		roots.forEach[r | 
+
+		roots.forEach [ r |
 			Assert.isTrue(r.children !== null)
 			Assert.isTrue(r.children.length == 0)
 			if (! r.children.addAll(newNodes))
 				throw new RuntimeException()
 			Assert.isTrue(r.children.length > 0)
 		]
-		
-		roots.forEach[r | Assert.isTrue(r.children.length > 0, "Problem with " + r)]
-		
+
+		roots.forEach[r|Assert.isTrue(r.children.length > 0, "Problem with " + r)]
+
 		// move highestAltIdx to the next unmarked alternative
-		var newHighestIdx = highestAltIdx+1
-		while (newHighestIdx < sortedAlts.length && sortedAlts.get(newHighestIdx).inVariantTree){
+		var newHighestIdx = highestAltIdx + 1
+		while (newHighestIdx < sortedAlts.length && sortedAlts.get(newHighestIdx).inVariantTree) {
 			newHighestIdx++
 		}
-		
+
 		// Go for next recursion: take all the newly created notes into a list, and call recursively.
-		recCreateVariantTree(newNodes, sortedAlts, newHighestIdx, cs, id+newNodes.length)
+		recCreateVariantTree(newNodes, sortedAlts, newHighestIdx, cs, id + newNodes.length)
 	}
-	
-	def private collectMarkSiblings(MarkedAlternative highestAlt, ArrayList<MarkedAlternative> sortedAlts, int highestAltIdx) {
-		
+
+	def private collectMarkSiblings(MarkedAlternative highestAlt, ArrayList<MarkedAlternative> sortedAlts,
+		int highestAltIdx) {
+
 		val r = newLinkedList(highestAlt)
-		
+
 		// First considers the different subtypes of alternative.
 		switch highestAlt.alternative {
 			RootCandidateScenario: {
 				// Case Scenario, the siblings are the other scenarios.
-				for (var i = highestAltIdx+1; i < sortedAlts.length; i++){
-					if (!sortedAlts.get(i).inVariantTree && sortedAlts.get(i).alternative instanceof RootCandidateScenario) {
+				for (var i = highestAltIdx + 1; i < sortedAlts.length; i++) {
+					if (!sortedAlts.get(i).inVariantTree &&
+						sortedAlts.get(i).alternative instanceof RootCandidateScenario) {
 						r.add(sortedAlts.get(i))
 						sortedAlts.get(i).inVariantTree = true
 					}
@@ -434,11 +467,10 @@ class CandidatesGenerator {
 				// Case adaptation, it only has siblings if there is a parent, and the siblings have the same parent.
 				val parent = (highestAlt.alternative as UnitAdaptation).parent
 				if (parent !== null) {
-					for (var i = highestAltIdx+1; i < sortedAlts.length; i++){
-						if (!sortedAlts.get(i).inVariantTree && 
-								sortedAlts.get(i).alternative instanceof UnitAdaptation &&
-								(sortedAlts.get(i).alternative as UnitAdaptation).parent === parent
-						) {
+					for (var i = highestAltIdx + 1; i < sortedAlts.length; i++) {
+						if (!sortedAlts.get(i).inVariantTree &&
+							sortedAlts.get(i).alternative instanceof UnitAdaptation &&
+							(sortedAlts.get(i).alternative as UnitAdaptation).parent === parent) {
 							r.add(sortedAlts.get(i))
 							sortedAlts.get(i).inVariantTree = true
 						}
@@ -449,21 +481,20 @@ class CandidatesGenerator {
 				// Case adaptation, it only has siblings if there is a parent, and the siblings have the same parent.
 				val parent = (highestAlt.alternative as PortAdaptation).parent
 				if (parent !== null) {
-					for (var i = highestAltIdx+1; i < sortedAlts.length; i++){
-						if (!sortedAlts.get(i).inVariantTree && 
-								sortedAlts.get(i).alternative instanceof PortAdaptation &&
-								(sortedAlts.get(i).alternative as PortAdaptation).parent === parent
-						) {
+					for (var i = highestAltIdx + 1; i < sortedAlts.length; i++) {
+						if (!sortedAlts.get(i).inVariantTree &&
+							sortedAlts.get(i).alternative instanceof PortAdaptation &&
+							(sortedAlts.get(i).alternative as PortAdaptation).parent === parent) {
 							r.add(sortedAlts.get(i))
 							sortedAlts.get(i).inVariantTree = true
 						}
 					}
 				}
 			}
-			default: throw new UnsupportedOperationException()
+			default:
+				throw new UnsupportedOperationException()
 		}
 		return r
 	}
-	
-	
-}
+
+}