Commit c98932fd authored by Sebastian Vollbrecht's avatar Sebastian Vollbrecht

Refactored suffix creation, started refactoring edge creation phase.

parent c9fe4b45
......@@ -179,6 +179,7 @@ public class EdgeCreator extends Observable<Edge<ResourceNode>> implements EdgeC
outgoingBackedges.get(src).add(edge);
incomingBackedges.get(dst).add(edge);
// TODO: is this check necessary?
if (src != GraphGenerator.SOURCE && dst != GraphGenerator.SINK) {
notifyObservers(edge);
}
......
......@@ -507,7 +507,7 @@ public class FeasibleMinIIProperty extends Property {
remainingPathDelay = appendNodeToPath(remainingPathDelay, nodesByDelay, path);
}
distributeRemainingDelay(path.path, remainingPathDelay);
PlannedEdge.distributeRemainingDelay(path.path, remainingPathDelay, rng);
path.path.forEach(e -> edgeCreator.createEdge(e.src, e.dst, e.delay));
......
......@@ -22,16 +22,12 @@ import graphgen.generator.GraphGenerator;
import graphgen.generator.components.Initializable;
import graphgen.generator.components.edges.EdgeCreator;
import graphgen.generator.components.properties.infeasibleMinII.InfeasibleMinIIProperty;
import graphgen.generator.components.properties.util.PlannedEdge;
import graphgen.graph.LayerStructure;
import graphgen.graph.ResourceNode;
import graphgen.observer.Observer;
import graphgen.util.JavaUtils;
import modsched.Edge;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
......@@ -126,26 +122,4 @@ public abstract class Property implements Observer<Edge<ResourceNode>>, Initiali
@Override
public abstract void reset();
/**
* Distributes an amount of delay over a collection of planned edges.
*
* @param plannedEdges the collection of planned edges
* @param delay the delay to distribute
* @throws NullPointerException if the set of edges is null
* @throws IllegalArgumentException if the set of edges is empty and the delay to distribute is non-zero
*/
protected void distributeRemainingDelay(Collection<PlannedEdge> plannedEdges, int delay) {
if (Objects.requireNonNull(plannedEdges).isEmpty() && delay > 0) {
throw new IllegalArgumentException(
"Cannot distribute remaining delay of " + delay + " over non-existent edges.");
}
while (delay > 0) {
PlannedEdge edge = JavaUtils.pickRandomElement(plannedEdges, rng);
edge.delay++;
delay--;
}
}
}
......@@ -17,6 +17,11 @@
package graphgen.generator.components.properties.infeasibleMinII;
import graphgen.datastructures.SeededRandom;
import graphgen.generator.components.properties.infeasibleMinII.configuration.InfeasibleConfiguration;
import graphgen.generator.components.properties.infeasibleMinII.configuration.StartConfiguration;
import graphgen.generator.components.properties.infeasibleMinII.configuration.SuffixConfiguration;
import graphgen.generator.components.properties.util.PlannedEdge;
import graphgen.graph.LayerStructure;
import graphgen.graph.Resource;
import graphgen.graph.ResourceNode;
......@@ -25,10 +30,15 @@ import graphgen.util.JavaUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class InfeasibilityInspector {
......@@ -82,6 +92,237 @@ public class InfeasibilityInspector {
}
public static Set<ResourceNode> getPossibleDstNodes(int dstNodeLayer, StartConfiguration startCfg) {
Set<ResourceNode> possibleDstNodes = new HashSet<>();
Set<ResourceNode> nodes = new HashSet<>(Objects.requireNonNull(startCfg).getNodeTable().values());
Resource problematicResource = startCfg.getProblematicResource();
Map<Integer, Set<ResourceNode>> nodesByDelay = new HashMap<>();
for (ResourceNode node : nodes) {
nodesByDelay.putIfAbsent(node.getDelay(), new HashSet<>());
nodesByDelay.get(node.getDelay()).add(node);
}
/*
* Stores the delays of the remaining nodes if enough of the respective
* resource's nodes were to be chosen for the problematic resource. The
* delays are useful as we then don't have to regard every destination
* node candidate individually. This means that if a delay is a possible
* delay for the destination node, every node having this delay is
* (almost always) a destination node candidate too.
*/
Map<Integer, Integer> remainingDelays = getRemainingDelays(nodes, problematicResource);
long problematicResourceCount = nodes.stream().filter(n -> n.resource == problematicResource).count();
boolean noSpareNodesAvailable = problematicResourceCount == problematicResource.limit + 1;
boolean isProblematicDelayZero = problematicResource.delay == 0;
for (Entry<Integer, Set<ResourceNode>> nodesByDelayEntry : nodesByDelay.entrySet()) {
int dstNodeDelay = nodesByDelayEntry.getKey();
boolean isSameDelay = dstNodeDelay == problematicResource.delay;
boolean arrangementPossible = areDstNodeParametersValid(dstNodeDelay, dstNodeLayer, startCfg,
remainingDelays
);
if (arrangementPossible) {
/*
* If the problematic resource's delay is not zero, if the current destination
* node's delay equals the problematic resource's delay and if there are no
* spare nodes, only non-problematic nodes may become destination nodes.
*
* Otherwise, if a problematic node were picked to be the destination node in
* this case, no infeasible time slot containing all problematic nodes would
* be manageable anymore due to the problematic destination node having a
* delay > 0 and due to there being no spare nodes.
*/
if (!isProblematicDelayZero && isSameDelay && noSpareNodesAvailable) {
for (ResourceNode node : nodesByDelayEntry.getValue()) {
if (node.resource != problematicResource) {
possibleDstNodes.add(node);
}
}
} else {
possibleDstNodes.addAll(nodesByDelayEntry.getValue());
}
}
}
return possibleDstNodes;
}
private static boolean areDstNodeParametersValid(int dstNodeDelay, int dstNodeLayer, StartConfiguration startCfg,
Map<Integer, Integer> remainingDelays) {
Map<Integer, Integer> delayCounts = new HashMap<>();
for (ResourceNode node : Objects.requireNonNull(startCfg).getNodeTable().values()) {
JavaUtils.insertOrIncValue(delayCounts, node.getDelay());
}
Resource problematicResource = startCfg.getProblematicResource();
boolean isSameDelay = dstNodeDelay == problematicResource.delay;
boolean isProblematicDelayZero = problematicResource.delay == 0;
/*
* 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())
return false;
/*
* If only the problematic nodes have the current delay, we cannot use it if
* it is non-zero. We cannot choose the non-zero problematic nodes for our
* destination node because then the problematic nodes would automatically be
* placed in different slots compared to the destination node's slot (which
* would in this case need to be a problematic node as well).
*/
if (isSameDelay && dstNodeDelay > 0) {
if (delayCounts.get(dstNodeDelay) == problematicResource.limit + 1) {
return false;
}
}
/*
* Use the available zero delays to figure out whether or not the current delay
* can be a valid destination node delay. This is done later on by verifying
* that all problematic nodes can be placed in a single time slot.
*/
int zeroNodesRemaining = delayCounts.getOrDefault(0, 0);
int leftToPlace = problematicResource.limit + 1;
/*
* If the problematic resource's delay is zero and we are currently dealing with
* a delay of zero, we can immediately subtract 1 from the nodes left to place
* as one of these problematic nodes can then be used as the destination node.
*/
if (dstNodeDelay == 0) {
if (isProblematicDelayZero) {
leftToPlace--;
}
zeroNodesRemaining--;
}
/*
* Try to see if the problematic nodes can still all be placed in the same slot,
* beginning with the destination node's succeeding layer.
*/
int lastPossibleLayer = startCfg.getLayers().getHeight() - 1;
for (int lastProblematicLayer = dstNodeLayer + 1; lastProblematicLayer <= lastPossibleLayer;
lastProblematicLayer++) {
/*
* The current configuration can only be valid if all of the remaining problematic
* nodes could potentially be placed in the current layer.
*/
if (leftToPlace <= startCfg.getLayers().getLayer(lastProblematicLayer).size()) {
/*
* If the problematic resource's delay is zero and if the destination node's
* delay is zero as well, an additional subsequent end node is needed which the
* RecMinII-ensuring backedge can then extend from, since the path does not have
* any delay so far as the destination node and the problematic nodes all share
* the same time slot (the destination node is automatically treated as a
* 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.
*/
if (isProblematicDelayZero && isSameDelay && 0 < startCfg.getMaxInnerDelay()) {
if (lastProblematicLayer < lastPossibleLayer) {
/*
* We must check whether we have any end node candidates whose delays are small
* enough to not exceed the maximum inner delay.
*/
boolean candidateFound = false;
for (int remainingDelay : remainingDelays.keySet()) {
if (zeroNodesRemaining == 0 && remainingDelay == 0)
continue;
if (remainingDelay <= startCfg.getMaxInnerDelay()) {
candidateFound = true;
break;
}
}
return candidateFound;
} else {
return false;
}
}
return true;
}
/*
* 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.
*/
if (zeroNodesRemaining == 0)
return false;
/*
* Otherwise, place as many problematic nodes as possible in the current layer.
* If the problematic nodes' delay is zero, an additional problematic node can
* be placed without increasing the next layer's minimal ASAP time.
*/
int placedNodes = startCfg.getLayers().getLayer(lastProblematicLayer).size() - 1;
if (isProblematicDelayZero) {
zeroNodesRemaining -= ++placedNodes;
} else {
zeroNodesRemaining--;
}
leftToPlace -= placedNodes;
}
return false;
}
private static Map<Integer, Integer> getRemainingDelays(Set<ResourceNode> nodes, Resource problematicResource) {
Map<Integer, Integer> remainingDelays = new HashMap<>();
/*
* 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;
for (ResourceNode node : nodes) {
Resource resource = node.resource;
if (resource == problematicResource && skipsRequired > 0) {
skipsRequired--;
} else {
JavaUtils.insertOrIncValue(remainingDelays, resource.delay);
}
}
return remainingDelays;
}
public static Set<Integer> getPotentialLayers(ResourceNode problematicNode, InfeasibleConfiguration infeasibleCfg) {
int remainingZeroNodes = infeasibleCfg.getRemainingZeroNodes().size();
......@@ -224,7 +465,7 @@ public class InfeasibilityInspector {
}
boolean isDelayZero = nodeToPlace.getDelay() == 0;
int leftToPlace = infeasibleCfg.getLeftToPlace();
int leftToPlace = infeasibleCfg.howManyLeftToPlace();
int firstLayer = infeasibleCfg.getStartConfiguration().getDestinationNodeLayer() + 1;
for (int currentLayer = firstLayer; currentLayer < infeasibleCfg.getLayers().getHeight(); currentLayer++) {
......@@ -276,4 +517,406 @@ public class InfeasibilityInspector {
return Optional.empty();
}
public static int placeNode(ResourceNode problematicNode, InfeasibleConfiguration infeasibleCfg, SeededRandom rng) {
Set<Integer> possibleLayers = InfeasibilityInspector.getPotentialLayers(problematicNode, infeasibleCfg);
boolean useCurrentLayer = possibleLayers.contains(infeasibleCfg.getLayers().getDepth(problematicNode.getId()));
/*
* Determine the problematic node's target layer, based on our previous
* findings. Also move the node to its target layer if needed.
*/
int targetLayer;
if (!useCurrentLayer) {
targetLayer = JavaUtils.pickRandomElement(possibleLayers, rng);
} else {
targetLayer = infeasibleCfg.getLayers().getDepth(problematicNode.getId());
}
if (!useCurrentLayer) {
moveNodeToLayer(problematicNode, targetLayer, infeasibleCfg, rng);
}
infeasibleCfg.fixateNode(problematicNode);
return targetLayer;
}
public static void placeZeroNodesInBetween(int targetLayer, InfeasibleConfiguration infeasibleCfg,
SeededRandom rng) {
int firstLayer = infeasibleCfg.getStartConfiguration().getDestinationNodeLayer() + 1;
Set<ResourceNode> remainingZeroNodes = infeasibleCfg.getRemainingZeroNodes();
for (int layer = firstLayer; layer < targetLayer; layer++) {
if (infeasibleCfg.getZeroNodesByLayer().containsKey(layer))
continue;
Set<ResourceNode> presentNodesWithZeroDelay = new HashSet<>();
for (int id : infeasibleCfg.getLayers().getLayer(layer)) {
ResourceNode node = infeasibleCfg.getNodeTable().get(id);
if (node.getDelay() == 0) {
presentNodesWithZeroDelay.add(node);
}
}
ResourceNode zeroNode;
if (!presentNodesWithZeroDelay.isEmpty()) {
zeroNode = JavaUtils.removeRandomElement(presentNodesWithZeroDelay, rng);
remainingZeroNodes.remove(zeroNode);
} else {
zeroNode = JavaUtils.removeRandomElement(remainingZeroNodes, rng);
moveNodeToLayer(zeroNode, layer, infeasibleCfg, rng);
}
infeasibleCfg.fixateNode(zeroNode);
}
}
private static void moveNodeToLayer(ResourceNode node, int layer, InfeasibleConfiguration infeasibleCfg,
SeededRandom rng) {
Set<Integer> swappableIDs = new HashSet<>();
for (int id : infeasibleCfg.getLayers().getLayer(layer)) {
if (infeasibleCfg.getFixedNodes().contains(infeasibleCfg.getNodeTable().get(id))) {
swappableIDs.add(id);
}
}
infeasibleCfg.getLayers().swapIDs(node.getId(), JavaUtils.pickRandomElement(swappableIDs, rng));
}
public static Set<Integer> getProblematicTimeslots(StartConfiguration startCfg,
InfeasibleConfiguration infeasibleCfg,
SuffixConfiguration suffixCfg) {
Set<Integer> possibleTimeSlots = new HashSet<>();
ResourceNode destinationNode = startCfg.getDestinationNode();
if (infeasibleCfg.getProblematicNodes().contains(destinationNode)) {
possibleTimeSlots.add(0);
return possibleTimeSlots;
}
int firstSlot = destinationNode.getDelay();
int lastSlot = firstSlot + infeasibleCfg.getProblematicResource().delay + suffixCfg.getRemainingInnerDelay();
return JavaUtils.asSet(IntStream.rangeClosed(firstSlot, lastSlot).boxed());
}
public static Set<PlannedEdge> getNeededEdges(StartConfiguration startCfg, InfeasibleConfiguration infeasibleCfg,
SuffixConfiguration suffixCfg, SeededRandom rng) {
Set<PlannedEdge> plannedEdges = new HashSet<>();
LayerStructure layers = startCfg.getLayers();
/*
* Preparations for the distribution of the remaining inner delay over all
* suffix edges and the edges extending from the destination nodes. All other
* edges (i.e. the edges between the problematic nodes and the zero nodes) must
* have a delay of zero to not destroy the infeasibility.
*/
Map<ResourceNode, PlannedEdge> nodesToIncomingEdges = new HashMap<>();
for (int i = 0; i < suffixCfg.getSuffixNodes().size() - 1; i++) {
ResourceNode src = suffixCfg.getSuffixNodes().get(i);
ResourceNode dst = suffixCfg.getSuffixNodes().get(i + 1);
PlannedEdge edge = new PlannedEdge(src, dst, 0, 0);
plannedEdges.add(edge);
nodesToIncomingEdges.put(dst, edge);
}
/*
* Add one additional edge representing the edges extending from the destination
* node. If this edge's delay is increased, all similar edges must be assigned
* the exact same delay as well to ensure that all problematic nodes remain in
* the same time step. Only do this if the destination node is not problematic
* itself, as then these edges must have a delay of zero. Furthermore, do this
* for every edge connecting to the first suffix node as well.
*/
PlannedEdge firstEdgeRepresentative = null;
PlannedEdge lastEdgeRepresentative = null;
/*
* Stores the edges extending from the destination node.
*/
Set<PlannedEdge> firstEdges = new HashSet<>();
if (!infeasibleCfg.getProblematicNodes().contains(startCfg.getDestinationNode())) {
ResourceNode src = startCfg.getDestinationNode();
int firstProblematicLayer = startCfg.getDestinationNodeLayer() + 1;
for (ResourceNode dst : infeasibleCfg.getProblematicNodes()) {
if (layers.getDepth(dst.getId()) == firstProblematicLayer) {
PlannedEdge firstEdge = new PlannedEdge(src, dst, 0, 0);
firstEdges.add(firstEdge);
nodesToIncomingEdges.put(dst, firstEdge);
}
}
if (infeasibleCfg.getZeroNodesByLayer().containsKey(firstProblematicLayer)) {
for (ResourceNode dst : infeasibleCfg.getZeroNodesByLayer().get(firstProblematicLayer)) {
PlannedEdge firstEdge = new PlannedEdge(src, dst, 0, 0);
firstEdges.add(firstEdge);
nodesToIncomingEdges.put(dst, firstEdge);
}
}
firstEdgeRepresentative = firstEdges.iterator().next();
plannedEdges.add(firstEdgeRepresentative);
}
Map<Integer, List<ResourceNode>> nodesByDepth = infeasibleCfg.getProblematicNodes().stream()
.collect(Collectors.groupingBy(n -> layers.getDepth(n.getId())));
Set<ResourceNode> lowestProblematicNodes = new HashSet<>(
nodesByDepth.get(Collections.max(nodesByDepth.keySet())));
/*
* Stores the edges going to the first suffix node.
*/
Set<PlannedEdge> lastEdges = new HashSet<>();
if (!suffixCfg.getSuffixNodes().isEmpty()) {
for (ResourceNode src : lowestProblematicNodes) {
PlannedEdge lastEdge = new PlannedEdge(src, suffixCfg.getSuffixNodes().get(0), 0, 0);
lastEdges.add(lastEdge);
nodesToIncomingEdges.put(lastEdge.dst, lastEdge);
}
lastEdgeRepresentative = lastEdges.iterator().next();
plannedEdges.add(lastEdgeRepresentative);
}
PlannedEdge.distributeRemainingDelay(plannedEdges, suffixCfg.getRemainingInnerDelay(), rng);
/*
* Copy the delay of the first edges' representative to the other first edges
* (and deal with the last edges analogously).
*/
for (PlannedEdge firstEdge : firstEdges) {
firstEdge.delay = firstEdgeRepresentative.delay;
}
for (PlannedEdge lastEdge : lastEdges) {
lastEdge.delay = lastEdgeRepresentative.delay;
}
if (!suffixCfg.getSuffixNodes().isEmpty()) {
suffixCfg.getSuffixOffsets().put(suffixCfg.getSuffixNodes().get(0), lastEdgeRepresentative.delay);
}
plannedEdges.addAll(firstEdges);
plannedEdges.addAll(lastEdges);
/*
* Adjust the suffix nodes' offsets (i.e. ASAP-wise from the last problematic
* nodes) based on the freshly distributed delay of the edges among them.
*/
for (int i = 0; i < suffixCfg.getSuffixNodes().size() - 1; i++) {
ResourceNode src = suffixCfg.getSuffixNodes().get(i);
ResourceNode dst = suffixCfg.getSuffixNodes().get(i + 1);
PlannedEdge edge = nodesToIncomingEdges.get(dst);
suffixCfg.getSuffixOffsets().put(dst, suffixCfg.getSuffixOffsets().get(src) + src.getDelay() + edge.delay);
}
return plannedEdges;
}
public static void addIncomingProblematicEdges(Set<PlannedEdge> problematicEdges, StartConfiguration startCfg,
InfeasibleConfiguration infeasibleCfg, SuffixConfiguration suffixCfg,
SeededRandom rng) {
/*
* We need to create an incoming edge for every fixed node. The source node can
* either be the destination node or a zero node of the previous layer.
*
* The edge's delay must be chosen carefully so as not to exceed the RecMinII:
* If the source node is not the destination node, the edge's delay must always
* be zero
*
* TODO: varying delay from destination node to very first zero nodes (this can
* then be distributed over the edges extending from the zero node without
* affecting the infeasibility)
*/
for (ResourceNode fixedNode : infeasibleCfg.getFixedNodes()) {
/*
* The destination node doesn't need an incoming edge (at least during property
* creation).
*/
if (fixedNode == startCfg.getDestinationNode()) {
continue;