Commit c2875b9e authored by Sebastian Vollbrecht's avatar Sebastian Vollbrecht

Added JavaDoc to the new classes.

Also moved some code around, renamed some things here and there, fixed some tests, ... Many little changes. You know how it is.
parent f102241f
......@@ -29,12 +29,18 @@ import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
/**
* This class contains some utility methods related to inspecting nodes and layer structure regarding possible
* infeasible node arrangements.
*
* @author Sebastian Vollbrecht
*/
public class InfeasibilityInspector {
/**
* Computes possible problematic resources whose limit could be exceeded in a cycle, using the provided set of
* nodes. The maximum inner delay is taken into account to discard resources whose delay exceeds the maximum inner
* delay already. A cycle consisting of these resources would automatically exceed the specified MinII.
* delay already. A cycle consisting of these resources would automatically exceed corresponding MinIIs.
*
* @param nodes the set of nodes
* @param maxInnerDelay the cycle's maximum inner delay
......@@ -81,6 +87,16 @@ public class InfeasibilityInspector {
}
/**
* Computes all destination nodes that could potentially be placed in the specified layer and serve as the
* destination node for the backedges of an infeasible cycle. The infeasible cycle's parameters (i.e. problematic
* resource, layer structure, node table and the maximum inner delay) are extracted from the provided start
* configuration.
*
* @param dstNodeLayer the layer for which possible possible destination nodes are needed
* @param startCfg the start configuration containing the cycle's parameters
* @return a set containing all potential destination node candidates
*/
public static Set<ResourceNode> getPossibleDstNodes(int dstNodeLayer, StartConfiguration startCfg) {
Set<ResourceNode> possibleDstNodes = new HashSet<>();
......@@ -166,8 +182,9 @@ public class InfeasibilityInspector {
* If the current destination node's delay makes it impossible to ensure the
* RecMinII, the delay is not valid.
*/
if (dstNodeDelay + problematicResource.delay > startCfg.getMaxInnerDelay())
if (dstNodeDelay + problematicResource.delay > startCfg.getMaxInnerDelay()) {
return false;
}
/*
* If only the problematic nodes have the current delay, we cannot use it if
......@@ -227,7 +244,8 @@ public class InfeasibilityInspector {
* problematic node if the resource's delay is zero).
*
* If no additional end node can be used, the maximum inner delay will be
* impossible to ensure and the destination node's delay/layer are invalid.
* impossible to ensure (as there wouldn't be any edges available to distribute
* the remaining delay over) and the destination node's delay/layer are invalid.
*/
if (isProblematicDelayZero && isSameDelay && 0 < startCfg.getMaxInnerDelay()) {
if (lastProblematicLayer < lastPossibleLayer) {
......@@ -240,8 +258,9 @@ public class InfeasibilityInspector {
for (int remainingDelay : remainingDelays.keySet()) {
if (zeroNodesRemaining == 0 && remainingDelay == 0)
if (zeroNodesRemaining == 0 && remainingDelay == 0) {
continue;
}
if (remainingDelay <= startCfg.getMaxInnerDelay()) {
candidateFound = true;
......@@ -264,10 +283,11 @@ public class InfeasibilityInspector {
/*
* If there are no nodes left with a delay of zero and if not all problematic
* nodes have yet been placed, the delay is impossible: we cannot use the next
* layer without increasing its minimal ASAP time.
* layer without automatically pushing its nodes in different timeslots.
*/
if (zeroNodesRemaining == 0)
if (zeroNodesRemaining == 0) {
return false;
}
/*
* Otherwise, place as many problematic nodes as possible in the current layer.
......@@ -293,7 +313,7 @@ public class InfeasibilityInspector {
Map<Integer, Integer> remainingDelays = new HashMap<>();
/*
* Skip an amount of nodes equal to (the resource's limit + 1) in order to compute the
* Skip an amount of nodes equal to [the resource's limit + 1] in order to compute the
* remaining delays if the resource were to be deemed the problematic resource.
*/
int skipsRequired = Objects.requireNonNull(problematicResource).limit + 1;
......
......@@ -17,6 +17,7 @@
package graphgen.generator.components.properties.infeasibleMinII;
import graphgen.datastructures.Pair;
import graphgen.datastructures.SeededRandom;
import graphgen.generator.components.properties.infeasibleMinII.configuration.InfeasibleConfiguration;
import graphgen.generator.components.properties.infeasibleMinII.configuration.StartConfiguration;
......@@ -39,6 +40,14 @@ import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.IntStream;
/**
* This class is responsible for planning how an infeasible arrangement of nodes can actually be made infeasible with
* edges interconnecting them. For example, it provides methods for retrieving possible incoming edges, for distributing
* certain delays over planned edges, for retrieving necessary outgoing edges, ... In other words, it deals with edge
* delay-specific calculations.
*
* @author Sebastian Vollbrecht
*/
public class InfeasibleEdgePlanner {
private final StartConfiguration startCfg;
......@@ -46,26 +55,40 @@ public class InfeasibleEdgePlanner {
private final SuffixConfiguration suffixCfg;
private final SeededRandom rng;
/**
* Creates a new edge planner, based on the provided configurations.
*
* @param startCfg the start configuration of the infeasible cycle
* @param infeasibleCfg the infeasible configuration of the cycle
* @param suffixCfg the suffix configuration of the cycle
* @param rng the random instance to use to when needed
*/
public InfeasibleEdgePlanner(StartConfiguration startCfg, InfeasibleConfiguration infeasibleCfg,
SuffixConfiguration suffixCfg, SeededRandom rng) {
this.startCfg = startCfg;
this.infeasibleCfg = infeasibleCfg;
this.suffixCfg = suffixCfg;
this.rng = rng;
this.startCfg = Objects.requireNonNull(startCfg);
this.infeasibleCfg = Objects.requireNonNull(infeasibleCfg);
this.suffixCfg = Objects.requireNonNull(suffixCfg);
this.rng = Objects.requireNonNull(rng);
}
/**
* Computes all timeslots which the problematic nodes could be placed in (relative to the destination node in slot
* zero) without exceeding the maximum inner delay of the infeasible cycle.
*
* @return a set containing all such timeslots
*/
public Set<Integer> getProblematicTimeslots() {
Set<Integer> possibleTimeSlots = new HashSet<>();
ResourceNode destinationNode = Objects.requireNonNull(startCfg).getDestinationNode();
ResourceNode destinationNode = startCfg.getDestinationNode();
if (Objects.requireNonNull(infeasibleCfg).getProblematicNodes().contains(destinationNode)) {
if (infeasibleCfg.getProblematicNodes().contains(destinationNode)) {
possibleTimeSlots.add(0);
return possibleTimeSlots;
}
if (Objects.requireNonNull(suffixCfg).getSuffixNodes().isEmpty()) {
if (suffixCfg.getSuffixNodes().isEmpty()) {
possibleTimeSlots.add(destinationNode.getDelay() + suffixCfg.getRemainingInnerDelay());
return possibleTimeSlots;
} else {
......@@ -77,31 +100,39 @@ public class InfeasibleEdgePlanner {
}
}
public Set<PlannedEdge> getIncomingEdges() {
/**
* Computes source-destination node pairs for every destination node which needs an incoming edge. These include
* problematic/zero nodes (source nodes can either be the destination node or other zero nodes) and the suffix nodes
* (source nodes can either be the lowest problematic nodes or another suffix node).<p>If there is more than valid
* predecessor for any of the problematic nodes, the method will choose a valid predecessor using the internal
* {@link SeededRandom} instance.
*
* @return the set of source-destination node pairs representing future edges
*/
public Set<Pair<ResourceNode, ResourceNode>> getIncomingEdgePairs() {
Set<PlannedEdge> edges = new HashSet<>();
Set<Pair<ResourceNode, ResourceNode>> pairs = new HashSet<>();
Map<ResourceNode, Set<PlannedEdge>> incomingEdgesByNode = new HashMap<>();
Map<ResourceNode, Set<PlannedEdge>> outgoingEdgesByNode = new HashMap<>();
Map<ResourceNode, ResourceNode> predecessorsByNode = new HashMap<>();
Map<ResourceNode, Set<ResourceNode>> successorsByNode = new HashMap<>();
/* Generate an incoming edge for every fixed node. */
/* Pick a predecessor for every fixed node (except for the destination node). */
for (ResourceNode fixedNode : infeasibleCfg.getFixedNodes()) {
if (incomingEdgesByNode.containsKey(fixedNode) || fixedNode == startCfg.getDestinationNode()) {
if (predecessorsByNode.containsKey(fixedNode) || fixedNode == startCfg.getDestinationNode()) {
continue;
}
Set<ResourceNode> predecessors = getPossiblePredecessors(fixedNode, startCfg, infeasibleCfg);
ResourceNode predecessor = JavaUtils.pickRandomElement(predecessors, rng);
PlannedEdge edge = new PlannedEdge(predecessor, fixedNode, 0, 0);
Pair<ResourceNode, ResourceNode> pair = Pair.makePair(predecessor, fixedNode);
incomingEdgesByNode.putIfAbsent(fixedNode, new HashSet<>());
incomingEdgesByNode.get(fixedNode).add(edge);
predecessorsByNode.put(fixedNode, predecessor);
outgoingEdgesByNode.putIfAbsent(predecessor, new HashSet<>());
outgoingEdgesByNode.get(predecessor).add(edge);
successorsByNode.putIfAbsent(predecessor, new HashSet<>());
successorsByNode.get(predecessor).add(fixedNode);
edges.add(edge);
pairs.add(pair);
}
......@@ -113,34 +144,32 @@ public class InfeasibleEdgePlanner {
if (i == 0) {
for (ResourceNode src : infeasibleCfg.getLowestProblematicNodes()) {
PlannedEdge edge = new PlannedEdge(src, suffixNode, 0, 0);
Pair<ResourceNode, ResourceNode> pair = Pair.makePair(src, suffixNode);
incomingEdgesByNode.putIfAbsent(src, new HashSet<>());
incomingEdgesByNode.get(src).add(edge);
predecessorsByNode.put(suffixNode, src);
outgoingEdgesByNode.putIfAbsent(src, new HashSet<>());
outgoingEdgesByNode.get(src).add(edge);
successorsByNode.putIfAbsent(src, new HashSet<>());
successorsByNode.get(src).add(suffixNode);
edges.add(edge);
pairs.add(pair);
}
} else {
ResourceNode src = suffixCfg.getSuffixNodes().get(i - 1);
PlannedEdge edge = new PlannedEdge(src, suffixNode, 0, 0);
Pair<ResourceNode, ResourceNode> pair = Pair.makePair(src, suffixNode);
incomingEdgesByNode.putIfAbsent(suffixNode, new HashSet<>());
incomingEdgesByNode.get(suffixNode).add(edge);
predecessorsByNode.put(suffixNode, src);
outgoingEdgesByNode.putIfAbsent(src, new HashSet<>());
outgoingEdgesByNode.get(src).add(edge);
successorsByNode.putIfAbsent(src, new HashSet<>());
successorsByNode.get(src).add(suffixNode);
edges.add(edge);
pairs.add(pair);
}
}
return edges;
return pairs;
}
......@@ -157,6 +186,123 @@ public class InfeasibleEdgePlanner {
}
/**
* Distributes the cycle's remaining inner delay over the provided set of planned edges. This method makes sure that
* all problematic nodes are going to be end up in the same, provided timeslot. The set of edges <i>must</i> include
* an incoming edge for every problematic node (except for the destination node), for every zero node and for every
* suffix node (if present) to ensure the infeasibility. <p>If a suffix has been created, the provided set must
* furthermore contain an outgoing edge for the lowest problematic nodes (these edges can easily soak up parts of
* the remaining delay). The distribution process makes sure that these lowest edges are going to have the exact
* same delay so as not to introduce slack in any of the lowest problematic nodes.
*
* @param problematicTimeslot the problematic nodes' timeslot, needed to compute slacks and offsets
* @param edges the existing set of planned edges
* @return an optional containing the delay of the lowest edges if a suffix has been created, otherwise an empty
* optional
*/
public Optional<Integer> distributeDelay(int problematicTimeslot, Set<PlannedEdge> edges) {
if (problematicTimeslot < 0) {
throw new IllegalArgumentException("The problematic nodes' timeslot cannot be negative.");
} else if (problematicTimeslot + infeasibleCfg.getProblematicResource().delay > infeasibleCfg
.getMaxInnerDelay()) {
throw new IllegalArgumentException(
"Placing the nodes in the specified timeslot would exceed the maximum inner delay.");
}
Map<ResourceNode, PlannedEdge> incomingEdgesByNode = getIncomingEdgesMap(edges, infeasibleCfg.getFixedNodes());
for (ResourceNode dst : infeasibleCfg.getFixedNodes()) {
if (dst != startCfg.getDestinationNode() && !incomingEdgesByNode.containsKey(dst)) {
throw new IllegalArgumentException("Fixed node " + dst + " does not have any incoming edges.");
}
}
/*
* The delay we need to distribute over edges leading to the problematic nodes
* so that they all end up in the given problematic timeslot.
*/
int problematicEdgesDelay = problematicTimeslot - startCfg.getDestinationNode().getDelay();
/* The set of edges whose delay must not change anymore to not destroy the infeasibility. */
Set<PlannedEdge> fixedEdges = new HashSet<>();
/*
* Make every problematic node's timeslot problematic (i.e. distribute the
* required delay over the edges leading to it). We must sort the nodes by their
* depth so that possible problematic zero nodes will be handled before their
* successors. Otherwise it might be possible for the summed delay (delay to the
* zero node plus the delay to the successor) to exceed the required delay.
*/
for (ResourceNode node : JavaUtils.asSortedList(infeasibleCfg.getProblematicNodes(), Comparator
.comparingInt(n -> infeasibleCfg.getLayers().getDepth(n.getId())))) {
makeNodeSlotProblematic(node, problematicEdgesDelay, incomingEdgesByNode, fixedEdges);
}
List<ResourceNode> suffixNodes = suffixCfg.getSuffixNodes();
if (!suffixNodes.isEmpty()) {
/*
* Add an edge representing all edges connecting from the lowest problematic nodes
* to the first suffix node. These edges MUST have equal delays.
*/
Set<ResourceNode> lowestNodes = infeasibleCfg.getLowestProblematicNodes();
Set<ResourceNode> relevantNodes = new HashSet<>(lowestNodes);
relevantNodes.addAll(suffixNodes);
Map<ResourceNode, PlannedEdge> outgoingEdgesByNode = getOutgoingEdgesMap(edges, relevantNodes);
for (ResourceNode src : relevantNodes) {
if (!outgoingEdgesByNode.containsKey(src) && src != suffixNodes.get(suffixNodes.size() - 1)) {
throw new IllegalArgumentException("Node " + src + " does not have any outgoing edge.");
}
}
List<PlannedEdge> suffixChain = new ArrayList<>();
PlannedEdge representative = outgoingEdgesByNode.get(lowestNodes.iterator().next());
suffixChain.add(representative);
suffixChain.addAll(getSuffixEdgeChain(outgoingEdgesByNode));
int remainingDelay = suffixCfg.getRemainingInnerDelay() - (problematicTimeslot - startCfg
.getDestinationNode().getDelay());
PlannedEdge.distributeDelay(suffixChain, remainingDelay, rng);
for (ResourceNode lowestNode : lowestNodes) {
outgoingEdgesByNode.get(lowestNode).delay = representative.delay;
}
return Optional.of(representative.delay);
}
return Optional.empty();
}
/**
* Computes a set of outgoing edges for the cycle's nodes which would both fulfill the minimally needed edge
* requirement that every node needs at least one outgoing edge <i>and</i> which would still guarantee the
* infeasibility of the cycle. The calculation takes into account the timeslot the problematic nodes are located in
* and the suffix nodes' offsets (relative to the destination node). If any of the problematic/zero/suffix nodes
* already has an outgoing edge (as specified by the given predicate), it will be skipped. The provided delay
* computer is used to compute possible edge delays, whose validity is then analyzed.<p>More formally, a problematic
* node's outgoing edge can either be an edge connecting to other fixed nodes/suffix nodes, or it can be a backedge
* connecting to the destination node, thereby reducing the fixed node's slack to zero as well.<p>A problematic node
* may however only connect to other problematic nodes if both the edge's delay and the problematic resource's delay
* are zero. Otherwise, the target node's ASAP time would increase and it would no longer share the problematic slot
* with the other nodes.<p>Furthermore, a problematic node may only connect to suffix nodes if the edge delay of the
* resulting edge would reduce the problematic node's slack to zero.<p> If no possible destination node can be found
* for any problematic node, it will be connected to the first suffix node (if present) to keep the edge delay
* minimal. If no suffix has been created, the node must become a backedge source.
*
* @param problematicTimeslot the problematic nodes' timeslot, needed to compute slacks and offsets
* @param nodeNeedsOutgoingEdge a predicate specifying which nodes still need an outgoing edge
* @param delayComputer the delay computer to use to compute possible edge delays
* @return a set of outgoing edges with their delays set as needed
*/
public Set<PlannedEdge> getOutgoingEdges(int problematicTimeslot, Predicate<ResourceNode> nodeNeedsOutgoingEdge,
BiFunction<ResourceNode, ResourceNode, Integer> delayComputer) {
......@@ -258,88 +404,6 @@ public class InfeasibleEdgePlanner {
return problematicEdges;
}
public Optional<PlannedEdge> distributeDelay(int problematicTimeslot, Set<PlannedEdge> edges) {
if (problematicTimeslot < 0) {
throw new IllegalArgumentException("The problematic nodes' timeslot cannot be negative.");
} else if (problematicTimeslot + infeasibleCfg.getProblematicResource().delay > infeasibleCfg
.getMaxInnerDelay()) {
throw new IllegalArgumentException(
"Placing the nodes in the specified timeslot would exceed the maximum inner delay.");
}
Map<ResourceNode, PlannedEdge> incomingEdgesByNode = getIncomingEdgesMap(edges, infeasibleCfg.getFixedNodes());
for (ResourceNode dst : infeasibleCfg.getFixedNodes()) {
if (dst != startCfg.getDestinationNode() && !incomingEdgesByNode.containsKey(dst)) {
throw new IllegalArgumentException("Fixed node " + dst + " does not have any incoming edges.");
}
}
/*
* The delay we need to distribute over edges leading to the problematic nodes
* so that they all end up in the given problematic timeslot.
*/
int problematicEdgesDelay = problematicTimeslot - startCfg.getDestinationNode().getDelay();
/* The set of edges whose delay must not change anymore to not destroy the infeasibility. */
Set<PlannedEdge> fixedEdges = new HashSet<>();
/*
* Make every problematic node's timeslot problematic (i.e. distribute the
* required delay over the edges leading to it). We must sort the nodes by their
* depth so that possible problematic zero nodes will be handled before their
* successors. Otherwise it might be possible for the summed delay (delay to the
* zero node plus the delay to the successor) to exceed the required delay.
*/
for (ResourceNode node : JavaUtils.asSortedList(infeasibleCfg.getProblematicNodes(), Comparator
.comparingInt(n -> infeasibleCfg.getLayers().getDepth(n.getId())))) {
makeNodeSlotProblematic(node, problematicEdgesDelay, incomingEdgesByNode, fixedEdges);
}
List<ResourceNode> suffixNodes = suffixCfg.getSuffixNodes();
if (!suffixNodes.isEmpty()) {
/*
* Add an edge representing all edges connecting from the lowest problematic nodes
* to the first suffix node. These edges MUST have equal delays.
*/
Set<ResourceNode> lowestNodes = infeasibleCfg.getLowestProblematicNodes();
Set<ResourceNode> relevantNodes = new HashSet<>(lowestNodes);
relevantNodes.addAll(suffixNodes);
Map<ResourceNode, PlannedEdge> outgoingEdgesByNode = getOutgoingEdgesMap(edges, relevantNodes);
for (ResourceNode src : relevantNodes) {
if (!outgoingEdgesByNode.containsKey(src) && src != suffixNodes.get(suffixNodes.size() - 1)) {
throw new IllegalArgumentException("Node " + src + " does not have any outgoing edge.");
}
}
List<PlannedEdge> suffixChain = new ArrayList<>();
PlannedEdge representative = outgoingEdgesByNode.get(lowestNodes.iterator().next());
suffixChain.add(representative);
suffixChain.addAll(getSuffixEdgeChain(outgoingEdgesByNode));
int remainingDelay = suffixCfg.getRemainingInnerDelay() - (problematicTimeslot - startCfg
.getDestinationNode().getDelay());
PlannedEdge.distributeDelay(suffixChain, remainingDelay, rng);
for (ResourceNode lowestNode : lowestNodes) {
outgoingEdgesByNode.get(lowestNode).delay = representative.delay;
}
return Optional.of(representative);
}
return Optional.empty();
}
private List<PlannedEdge> getSuffixEdgeChain(Map<ResourceNode, PlannedEdge> outgoingEdgesByNode) {
List<PlannedEdge> chain = new ArrayList<>();
......@@ -381,13 +445,22 @@ public class InfeasibleEdgePlanner {
}
/**
* Maps nodes to their incoming edge, based on the provided set of edges. The set containing the relevant nodes is
* used to determine which nodes to include in the returned map.
*
* @param edges the set of edges
* @param relevantNodes the nodes to consider
* @return a mapping of relevant nodes to their incoming edge
* @throws IllegalArgumentException if any of the relevant nodes has more than one incoming edge or none at all
*/
public static Map<ResourceNode, PlannedEdge> getIncomingEdgesMap(Set<PlannedEdge> edges,
Collection<ResourceNode> relevantNodes) {
Map<ResourceNode, PlannedEdge> incomingEdgesByNode = new HashMap<>();
for (PlannedEdge edge : Objects.requireNonNull(edges)) {
if (relevantNodes.contains(edge.src) && relevantNodes.contains(edge.dst)) {
if (Objects.requireNonNull(relevantNodes).contains(edge.src) && relevantNodes.contains(edge.dst)) {
if (incomingEdgesByNode.containsKey(edge.dst)) {
throw new IllegalArgumentException("Node " + edge.dst + " must have exactly one incoming edge.");
......@@ -400,12 +473,21 @@ public class InfeasibleEdgePlanner {
return incomingEdgesByNode;
}
/**
* Maps nodes to their outgoing edge, based on the provided set of edges. The set containing the relevant nodes is
* used to determine which nodes to include in the returned map.
*
* @param edges the set of edges
* @param relevantNodes the nodes to consider
* @return a mapping of relevant nodes to their outgoing edge
* @throws IllegalArgumentException if any of the relevant nodes has more than one outgoing edge or none at all
*/
public static Map<ResourceNode, PlannedEdge> getOutgoingEdgesMap(Set<PlannedEdge> edges,
Collection<ResourceNode> relevantNodes) {
Map<ResourceNode, PlannedEdge> outgoingEdgesByNode = new HashMap<>();
for (PlannedEdge edge : Objects.requireNonNull(edges)) {
if (relevantNodes.contains(edge.src) && relevantNodes.contains(edge.dst)) {
if (Objects.requireNonNull(relevantNodes).contains(edge.src) && relevantNodes.contains(edge.dst)) {
if (outgoingEdgesByNode.containsKey(edge.src)) {
throw new IllegalArgumentException("Node " + edge.src + " must have exactly one outgoing edge.");
......
......@@ -17,6 +17,7 @@
package graphgen.generator.components.properties.infeasibleMinII;
import graphgen.datastructures.Pair;
import graphgen.generator.components.properties.Property;
import graphgen.generator.components.properties.infeasibleMinII.configuration.InfeasibleConfiguration;
import graphgen.generator.components.properties.infeasibleMinII.configuration.StartConfiguration;
......@@ -282,14 +283,7 @@ public class InfeasibleMinIIProperty extends Property {
InfeasibleNodePlacer placer = new InfeasibleNodePlacer(startCfg, infeasibleCfg, rng);
/*
* If the destination node's layer is currently not the chosen layer, we'll have
* to move the node to the destined layer by swapping it with any of the destined
* layer's nodes.
*/
if (layers.getDepth(startCfg.getDestinationNode().getId()) != startCfg.getDestinationNodeLayer()) {
placer.placeNode(startCfg.getDestinationNode(), JavaUtils.asSet(startCfg.getDestinationNodeLayer()));
}
placer.placeNode(startCfg.getDestinationNode(), JavaUtils.asSet(startCfg.getDestinationNodeLayer()));
/*
* Gather all remaining nodes which use the problematic resource and randomly
......@@ -394,7 +388,10 @@ public class InfeasibleMinIIProperty extends Property {
*/
InfeasibleEdgePlanner edgePlanner = new InfeasibleEdgePlanner(startCfg, infeasibleCfg, suffixCfg, rng);
Set<PlannedEdge> plannedEdges = edgePlanner.getIncomingEdges();
/* Stores pairs of source and destination nodes which need to be connected with an edge. */
Set<Pair<ResourceNode, ResourceNode>> edgePairs = edgePlanner.getIncomingEdgePairs();
Set<PlannedEdge> plannedEdges = new HashSet<>();
edgePairs.forEach(p -> plannedEdges.add(new PlannedEdge(p.first, p.second, 0, 0)));
/* Choose the timeslot of the problematic nodes (relative to the destination node). */
Set<Integer> possibleTimeslots = edgePlanner.getProblematicTimeslots();
......@@ -405,25 +402,26 @@ public class InfeasibleMinIIProperty extends Property {
* the chosen timeslot. The remaining inner delay will also be distributed over the
* non-problematic edges (e.g. the suffix edges).
*/
Optional<PlannedEdge> lastEdgeRepresentative = edgePlanner.distributeDelay(problematicTimeslot, plannedEdges);
Optional<Integer> lowestEdgesDelay = edgePlanner.distributeDelay(problematicTimeslot, plannedEdges);
/*
* Adjust the suffix nodes' offsets (i.e. ASAP-wise from the destination node)
* based on the freshly distributed delay of the edges among them. These are
* useful for the creation of the cycle's required outgoing edges below.
*/
if (lastEdgeRepresentative.isPresent()) {
if (lowestEdgesDelay.isPresent()) {
int lowestEdgeDelay = lowestEdgesDelay.get();
Map<ResourceNode, PlannedEdge> outgoingEdgesByNode = InfeasibleEdgePlanner
.getOutgoingEdgesMap(plannedEdges, suffixCfg.getSuffixNodes());
int firstSuffixSlot = problematicTimeslot + infeasibleCfg.getProblematicResource().delay + lowestEdgeDelay;
int firstSuffixSlot = problematicTimeslot + infeasibleCfg
.getProblematicResource().delay + lastEdgeRepresentative.get().delay;
suffixCfg.setInitialOffset(firstSuffixSlot);
for (int i = 0; i < suffixCfg.getSuffixNodes().size() - 1; i++) {
ResourceNode src = suffixCfg.getSuffixNodes().get(i);
suffixCfg.addPlannedEdge(outgoingEdgesByNode.get(src));
suffixCfg.updateSuffixOffsets(outgoingEdgesByNode.get(src));
}
}
......
......@@ -30,18 +30,41 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
/**
* This class is responsible for arranging nodes in an infeasible fashion. It provides methods for determining possible
* layers a problematic node could be placed in and offers a method for placing them there as well.
*
* @author Sebastian Vollbrecht
*/
public class InfeasibleNodePlacer {
private final StartConfiguration startCfg;
private final InfeasibleConfiguration infeasibleCfg;
private final SeededRandom rng;
/**
* Creates a new node placer, based on the provided configurations.
*
* @param startCfg the start configuration of the infeasible cycle
* @param infeasibleCfg the infeasible configuration of the cycle
* @param rng the random instance to use to when needed
*/
public InfeasibleNodePlacer(StartConfiguration startCfg, InfeasibleConfiguration infeasibleCfg, SeededRandom rng) {
this.startCfg = startCfg;
this.infeasibleCfg = infeasibleCfg;
this.rng = rng;
this.startCfg = Objects.requireNonNull(startCfg);
this.infeasibleCfg = Objects.requireNonNull(infeasibleCfg);
this.rng = Objects.requireNonNull(rng);
}
/**
* Computes all layers where the given problematic node could be placed without making it impossible for the
* remaining problematic nodes to be placed in the same timeslot. The calculation takes into account the non-fixed
* zero nodes to find out which layers are reachable and would remain reachable if the node were placed somewhere.
*
* @param problematicNode the problematic node to determine possible layers for
* @return a set of layers where the node could be placed
* @throws IllegalArgumentException if the node is not contained in the set of problematic nodes or if it has been
* fixed already
*/
public Set<Integer> getPossibleLayers(ResourceNode problematicNode) {
if (!infeasibleCfg.getProblematicNodes().contains(Objects.requireNonNull(problematicNode))) {
......@@ -150,20 +173,31 @@ public class InfeasibleNodePlacer {
}
public void placeNode(ResourceNode problematicNode, Set<Integer> possibleLayers) {
/**
* Places a node in a certain layer, using the given set of target layers and automatically pads the preceding
* layers of the node's target layers with zero nodes if needed. The node must either be a problematic node or the
* destination node, as all other nodes don't need to be moved manually at all. If the node's current layer is
* contained in the set of target layers, it will always be chosen so as to minimize changes to the generated graph.
* Otherwise, the target layer will be picked randomly.
*
* @param problematicNode the node to place
* @param targetLayers the set of possible layers the node could be placed in
* @throws IllegalArgumentException if the set of possible layers is empty, if the node is neither problematic nor
* the destination node or if any of the possible layers are invalid
*/
public void placeNode(ResourceNode problematicNode, Set<Integer> targetLayers) {
if (possibleLayers.isEmpty()) {
throw new IllegalArgumentException("Possible layers must be non-empty.");
if (targetLayers.isEmpty()) {
throw new IllegalArgumentException("Target layers must be non-empty.");
} else if (problematicNode != startCfg.getDestinationNode()) {
if (possibleLayers.stream().anyMatch(l -> l <= startCfg.getDestinationNodeLayer())) {
throw new IllegalArgumentException(
"Possible layers must be greater than the destination node's layer.");
if (targetLayers.stream().anyMatch(l -> l <= startCfg.getDestinationNodeLayer())) {