|
|
@@ -8,26 +8,6 @@ import java.util.Set
|
|
|
import org.eclipse.core.runtime.Assert
|
|
|
import org.slf4j.Logger
|
|
|
import org.slf4j.LoggerFactory
|
|
|
-import ua.ansymo.hintco.Alternative
|
|
|
-import ua.ansymo.hintco.HintcoFactory
|
|
|
-import ua.ansymo.hintco.CosimUnitInstance
|
|
|
-import ua.ansymo.hintco.DecompositionPortAdaptation
|
|
|
-import ua.ansymo.hintco.DecompositionUnitAdaptation
|
|
|
-import ua.ansymo.hintco.ExtrapolationAdaptation
|
|
|
-import ua.ansymo.hintco.HierarchicalCosimUnit
|
|
|
-import ua.ansymo.hintco.HintConfiguration
|
|
|
-import ua.ansymo.hintco.InputPortInstance
|
|
|
-import ua.ansymo.hintco.InterpolationAdaptation
|
|
|
-import ua.ansymo.hintco.MultiRateAdaptation
|
|
|
-import ua.ansymo.hintco.OutputPortInstance
|
|
|
-import ua.ansymo.hintco.PortAdaptation
|
|
|
-import ua.ansymo.hintco.PortInstance
|
|
|
-import ua.ansymo.hintco.PowerBondAdaptation
|
|
|
-import ua.ansymo.hintco.PrecendenceNode
|
|
|
-import ua.ansymo.hintco.RootCandidateScenario
|
|
|
-import ua.ansymo.hintco.UnitAdaptation
|
|
|
-import ua.ansymo.hintco.UnitInstance
|
|
|
-import ua.ansymo.hintco.VariantDiagram
|
|
|
|
|
|
class CandidatesGenerator {
|
|
|
|
|
|
@@ -48,7 +28,7 @@ class CandidatesGenerator {
|
|
|
* Recursively goes through every path on the variant diagram and creates the necessary precedence constraints, cleaning up after the function call.
|
|
|
Optimizations: 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
|
|
|
Simple solution is to check if there already is a path connecting those two ports being connected.
|
|
|
- Compute transitive closure for quicker reachable computation after adding multiple edges. This result is passed down function calls, and cleaned up as well.
|
|
|
+ (Not done) Compute transitive closure for quicker reachable computation after adding multiple edges. This result is passed down function calls, and cleaned up as well.
|
|
|
The base case, for now, is to check whether there is a single path passing through all nodes, in a DFS, and return the first such path (mark it on the model, and call semantic adaptation code generation).
|
|
|
*/
|
|
|
|
|
|
@@ -139,6 +119,8 @@ class CandidatesGenerator {
|
|
|
println()
|
|
|
}
|
|
|
|
|
|
+ // TODO: Break this method into multiple methods: generation of contratins, propagation of constraints, etc...
|
|
|
+ // Leave the recursive calls here!
|
|
|
def private int recGenerateVariants(Deque<VariantDiagram> ns,
|
|
|
HintConfiguration cs,
|
|
|
ConstraintsStack constraints, int maxVariants) {
|
|
|
@@ -162,40 +144,61 @@ class CandidatesGenerator {
|
|
|
*/
|
|
|
val scenario = (alternative as RootCandidateScenario)
|
|
|
logger.debug("Processing Alternative RootCandidateScenario {}", scenario.identifier)
|
|
|
- // Outports/HierarchicalPorts -> InPorts/HierarchicalPorts
|
|
|
+
|
|
|
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)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // Create I/DoStep constraints: inputs should be set before doStep.
|
|
|
- for (inP : scenario.eAllContents.filter(InputPortInstance)
|
|
|
- .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.
|
|
|
-
|
|
|
+ // 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 {}.{}->{}.", inP.unit.identifier, inP.identifier, inP.unit.identifier)
|
|
|
- val success = addConstraint(inP, inP.unit, constraints, n, cs)
|
|
|
+ logger.debug("Attempting to add constraint {}->{}.{}.", outP.unit.identifier, outP.unit.identifier, outP.identifier)
|
|
|
+ val success = addConstraint(outP.unit, outP, constraints, n, cs)
|
|
|
logger.debug("Success: {}", success)
|
|
|
}
|
|
|
|
|
|
- // The DoStep of each unit can only happen after the doStep of the parent unit.
|
|
|
+ // 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 {}->{}.", parentUnit.identifier, childUnit.identifier)
|
|
|
- val success = addConstraint(parentUnit, childUnit, constraints, n, cs)
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
ExtrapolationAdaptation: {
|
|
|
- // Add precedence constraints to the ports that provide value to this port.
|
|
|
- // and ensure the doStep ordering.
|
|
|
+ // 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)
|
|
|
@@ -203,54 +206,27 @@ class CandidatesGenerator {
|
|
|
Assert.isTrue(adapted instanceof InputPortInstance)
|
|
|
val adaptedPort = adapted as InputPortInstance
|
|
|
Assert.isTrue(adaptedPort.unit instanceof CosimUnitInstance)
|
|
|
- val srcPort = adaptedPort.realValueFrom
|
|
|
-
|
|
|
- val ancestorTriple = ModelQuery.findLeastAncestorScenarioTriple(adaptedPort, srcPort)
|
|
|
- val ancestorAdaptedUnit = ancestorTriple.get(1) as UnitInstance
|
|
|
- val ancestorSrcUnit = ancestorTriple.get(2) as UnitInstance
|
|
|
-
|
|
|
- // Redundant, as every port of the unit must be set before do step of the unit.
|
|
|
- // addConstraint(adaptedPort, adaptedUnit, constraints, n, cs)
|
|
|
- // Redundant, as the srcPort is connected to the adaptedPort
|
|
|
- // addConstraint(srcPort, adaptedPort, constraints, n, cs)
|
|
|
|
|
|
- var PortInstance siblingAdaptedPort = adaptedPort
|
|
|
- if (ancestorAdaptedUnit !== adaptedPort.unit) {
|
|
|
- siblingAdaptedPort = ancestorAdaptedUnit.ports.findFirst[p | adaptedPort.getsValueFrom(p)]
|
|
|
- }
|
|
|
- Assert.isTrue(siblingAdaptedPort !== null)
|
|
|
-
|
|
|
- logger.debug("Attempting to add constraint {}.{}->{}.", siblingAdaptedPort.unit.identifier, siblingAdaptedPort.identifier, ancestorSrcUnit.identifier)
|
|
|
- val success = addConstraint(siblingAdaptedPort, ancestorSrcUnit, constraints, n, cs)
|
|
|
+ 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
|
|
|
- val srcPort = adaptedPort.realValueFrom
|
|
|
- val ancestorTriple = ModelQuery.findLeastAncestorScenarioTriple(adaptedPort, srcPort)
|
|
|
- val ancestorSrcUnit = ancestorTriple.get(2) as UnitInstance
|
|
|
-
|
|
|
- var PortInstance ancestorSrcPort = srcPort
|
|
|
- if (ancestorSrcUnit !== srcPort.unit) {
|
|
|
- ancestorSrcPort = ancestorSrcUnit.ports.findFirst[p | adaptedPort.getsValueFrom(p)]
|
|
|
- }
|
|
|
-
|
|
|
|
|
|
-
|
|
|
- logger.debug("Attempting to add constraint {}->{}.{}.", ancestorSrcUnit.identifier, ancestorSrcPort.unit.identifier, ancestorSrcPort.identifier)
|
|
|
- val success = addConstraint(ancestorSrcUnit, ancestorSrcPort, constraints, n, cs)
|
|
|
+ logger.debug("Attempting to add constraint {}.{}->{}.{}.", adaptedPort.unit.identifier, adaptedPort.identifier, adaptedPort.unit.identifier)
|
|
|
+ val success = addConstraint(adaptedPort, adaptedPort.unit, constraints, n, cs)
|
|
|
logger.debug("Success: {}", success)
|
|
|
|
|
|
- // Redundant, as the srcPort is connected to the adaptedPort
|
|
|
- // addConstraint(srcPort, adaptedPort, constraints, n, cs)
|
|
|
-
|
|
|
- // Redundant, as every port of the unit must be set before do step of the unit.
|
|
|
- // addConstraint(adaptedPort, adaptedUnit, constraints, n, cs)
|
|
|
}
|
|
|
DecompositionPortAdaptation: {
|
|
|
// Nothing to do.
|
|
|
@@ -259,6 +235,11 @@ class CandidatesGenerator {
|
|
|
// 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)
|
|
|
@@ -271,6 +252,7 @@ class CandidatesGenerator {
|
|
|
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
|
|
|
@@ -289,6 +271,11 @@ class CandidatesGenerator {
|
|
|
if (n.children.empty){
|
|
|
// Base case
|
|
|
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]
|
|
|
// mark alternatives as selected, so the processor knows which alternatives have been selected.
|
|
|
// Exclude root because it has no alternative
|
|
|
@@ -323,6 +310,28 @@ class CandidatesGenerator {
|
|
|
return numGenVariants
|
|
|
}
|
|
|
|
|
|
+ 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){
|
|
|
+ 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))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
def markSelected(Deque<VariantDiagram> ns, boolean selected) {
|
|
|
for (n : ns){
|
|
|
if (n.alternative !== null){
|