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--;
}
}
}
/*
* Copyright 2018 Sebastian Vollbrecht, Julian Oppermann
* Embedded Systems and Applications Group, TU Darmstadt
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package graphgen.generator.components.properties.infeasibleMinII;
import graphgen.datastructures.SeededRandom;
import graphgen.graph.ResourceNode;
import graphgen.util.JavaUtils;
import java.util.HashSet;
import java.util.Set;
public class InfeasibleNodePlacer {
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> nodesWithZeroDelay = new HashSet<>();
for (int id : infeasibleCfg.getLayers().getLayer(layer)) {
ResourceNode node = infeasibleCfg.getNodeTable().get(id);
if (node.getDelay() == 0) {
nodesWithZeroDelay.add(node);
}
}
ResourceNode zeroNode;
if (!nodesWithZeroDelay.isEmpty()) {
zeroNode = JavaUtils.removeRandomElement(nodesWithZeroDelay, 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));
}
}
/*
* Copyright 2018 Sebastian Vollbrecht, Julian Oppermann
* Embedded Systems and Applications Group, TU Darmstadt
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package graphgen.generator.components.properties.infeasibleMinII;
import graphgen.graph.Resource;
import graphgen.graph.ResourceNode;
import graphgen.util.JavaUtils;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
public class StartConfigurationInspector {
public static Set<ResourceNode> getPossibleDstNodes(int dstNodeLayer, StartConfiguration startCfg) {
Set<ResourceNode> possibleDstNodes = new HashSet<>();
Set<ResourceNode> nodes = new HashSet<>(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;
}
}
......@@ -15,7 +15,7 @@
* limitations under the License.
*/
package graphgen.generator.components.properties.infeasibleMinII;
package graphgen.generator.components.properties.infeasibleMinII.configuration;
import graphgen.graph.LayerStructure;
import graphgen.graph.Resource;
......
......@@ -15,7 +15,7 @@
* limitations under the License.
*/
package graphgen.generator.components.properties.infeasibleMinII;
package graphgen.generator.components.properties.infeasibleMinII.configuration;
import graphgen.graph.ResourceNode;
import graphgen.util.JavaUtils;
......@@ -81,12 +81,19 @@ public class InfeasibleConfiguration extends Configuration {
}
public void addProblematicNode(ResourceNode problematicNode) {
if (problematicNodes.size() == problematicResource.limit + 1) {
throw new IllegalArgumentException("The set of problematic nodes contains enough nodes already.");
} else if (!problematicNodes.add(Objects.requireNonNull(problematicNode))) {
} else if (problematicNodes.contains(Objects.requireNonNull(problematicNode))) {
throw new IllegalArgumentException(
"The set of problematic nodes contains node " + problematicNode + " already.");
} else if (problematicNode == startCfg.getDestinationNode() && startCfg.getDestinationNode().getDelay() > 0) {
throw new IllegalArgumentException(
"The destination node cannot be considered problematic with a delay > 0.");
}
problematicNodes.add(problematicNode);
}
public StartConfiguration getStartConfiguration() {
......@@ -109,7 +116,7 @@ public class InfeasibleConfiguration extends Configuration {
return Collections.unmodifiableMap(zeroNodesByLayer);
}
public int getLeftToPlace() {
public int howManyLeftToPlace() {
return problematicResource.limit + 1 - MathUtils.sumInteger(problematicNodesAtLayer.values());
}
......
......@@ -15,7 +15,7 @@
* limitations under the License.
*/
package graphgen.generator.components.properties.infeasibleMinII;
package graphgen.generator.components.properties.infeasibleMinII.configuration;
import graphgen.graph.LayerStructure;
import graphgen.graph.Resource;
......
......@@ -15,15 +15,14 @@
* limitations under the License.
*/
package graphgen.generator.components.properties.infeasibleMinII;
package graphgen.generator.components.properties.infeasibleMinII.configuration;
import graphgen.datastructures.SeededRandom;
import graphgen.generator.components.properties.util.PlannedEdge;
import graphgen.graph.ResourceNode;
import graphgen.util.JavaUtils;
import modsched.Node;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
......@@ -31,9 +30,7 @@ import java.util.Set;
public class SuffixConfiguration extends Configuration {
private final Set<ResourceNode> lowestNodes;
private final List<ResourceNode> suffixNodes;
private SeededRandom rng = null;
/**
* The offset of the suffix nodes denotes their delay from the very first suffix node. It is useful to compute edge
......@@ -43,108 +40,48 @@ public class SuffixConfiguration extends Configuration {
private final Map<ResourceNode, Integer> suffixOffsets;
private final Set<ResourceNode> backedgeSources;
public SuffixConfiguration(InfeasibleConfiguration infeasibleCfg, int remainingInnerDelay) {
super(infeasibleCfg.getStartConfiguration());
this.suffixNodes = new ArrayList<>();
this.suffixOffsets = new HashMap<>();
this.backedgeSources = new HashSet<>();
/*
* Determine the lowest problematic nodes to check whether the RecMinII-ensuring
* backedge(s) must extend from them themselves or whether additional suffix
* nodes can be appended.
*/
this.lowestNodes = new HashSet<>();
int maxDepth = Integer.MIN_VALUE;
for (ResourceNode problematicNode : Objects.requireNonNull(infeasibleCfg).getProblematicNodes()) {
int nodeDepth = layers.getDepth(problematicNode.getId());
if (nodeDepth > maxDepth) {
maxDepth = nodeDepth;
lowestNodes.clear();
lowestNodes.add(problematicNode);
} else if (nodeDepth == maxDepth) {
lowestNodes.add(problematicNode);
}
}
/*
* Append suffix nodes to the cycle as long as possible. The suffix layers begin
* after the last problematic layer.
*
* Also pick a random double value which defines the probability to append a
* node with delay zero if the remaining inner delay is zero already.
*/
double pToAppendMoreZeroNodes = rng.nextDouble();
for (int layer = maxDepth + 1; layer < layers.getHeight(); layer++) {
Set<ResourceNode> candidates = new HashSet<>();
if (remainingInnerDelay == 0 && rng.nextDouble() <= pToAppendMoreZeroNodes)
break;
private final InfeasibleConfiguration infeasibleCfg;
for (int nodeID : layers.getLayer(layer)) {
ResourceNode candidate = nodeTable.get(nodeID);
if (candidate.getDelay() <= remainingInnerDelay) {
candidates.add(candidate);
}
}
public SuffixConfiguration(List<ResourceNode> suffixNodes, Set<ResourceNode> backedgeSources,
InfeasibleConfiguration infeasibleCfg) {
super(Objects.requireNonNull(infeasibleCfg).getStartConfiguration());
this.suffixNodes = Objects.requireNonNull(suffixNodes);
this.backedgeSources = Objects.requireNonNull(backedgeSources);
this.suffixOffsets = new HashMap<>();
this.infeasibleCfg = infeasibleCfg;
if (candidates.isEmpty())
break;
}
/*
* Append a randomly picked candidate to the suffix and update the remaining
* inner delay accordingly.
*/
ResourceNode candidate = JavaUtils.pickRandomElement(candidates, rng);
this.suffixNodes.add(candidate);
remainingInnerDelay -= candidate.getDelay();
public int getRemainingInnerDelay() {
return maxInnerDelay - problematicResource.delay - infeasibleCfg.getStartConfiguration().getDestinationNode()
.getDelay() - suffixNodes.stream().mapToInt(Node::getDelay).sum();
}
public void addPlannedEdge(PlannedEdge edge) {
if (!suffixNodes.contains(edge.src) && !suffixNodes.contains(edge.dst)) {
throw new IllegalArgumentException("The edge does not affect any suffix node.");
} else if (!suffixOffsets.containsKey(edge.src)) {
throw new IllegalArgumentException(
"Cannot add a suffix edge connecting to " + edge.dst + " before adding an edge connecting to " + edge.src);
}
/*
* If no suffix has been created, all problematic end nodes must become backedge
* sources themselves. Otherwise, the very last suffix node will be used as the
* only backedge's source node.
*
* Special care needs to be taken when the suffix is empty and the problematic
* resource's delay is not zero. In this case, every problematic node needs to
* be the source node of a backedge, as they aren't able to connect to any other
* node without exceeding the RecMinII. If the delay is zero however, the
* 'inner' problematic nodes can still connect to each other, hence only the
* lowest problematic nodes need to become backedge sources.
*/
if (this.suffixNodes.isEmpty()) {
if (Objects.requireNonNull(infeasibleCfg).getProblematicResource().delay == 0)
this.backedgeSources.addAll(this.lowestNodes);
else
this.backedgeSources.addAll(infeasibleCfg.getProblematicNodes());
} else {
this.backedgeSources.add(this.suffixNodes.get(this.suffixNodes.size() - 1));
for (int i = suffixNodes.indexOf(edge.src); i < suffixNodes.size() - 1; i++) {
ResourceNode dst = suffixNodes.get(i + 1);
suffixOffsets.put(dst, suffixOffsets.get(edge.src) + edge.src.getDelay() + edge.delay);
}
}
public Set<ResourceNode> getLowestNodes() {
return lowestNodes;
}
public List<ResourceNode> getSuffixNodes() {
return suffixNodes;
return Collections.unmodifiableList(suffixNodes);
}
public Map<ResourceNode, Integer> getSuffixOffsets() {
return suffixOffsets;
return Collections.unmodifiableMap(suffixOffsets);
}
public Set<ResourceNode> getBackedgeSources() {
return backedgeSources;
return Collections.unmodifiableSet(backedgeSources);
}
}