Commit 9354d739 authored by Sebastian Vollbrecht's avatar Sebastian Vollbrecht

Added new classes & tests related to the creation of feasible MinII cycles.

Also redesigned the corresponding type hierarchy (including the classes of the infeasible MinII cycles).
parent 26f9eb00
...@@ -15,43 +15,78 @@ ...@@ -15,43 +15,78 @@
* limitations under the License. * limitations under the License.
*/ */
package graphgen.generator.components.properties.minII.infeasible.configuration; package graphgen.generator.components.properties.minII;
import graphgen.graph.LayerStructure; import graphgen.graph.LayerStructure;
import graphgen.graph.Resource;
import graphgen.graph.ResourceNode; import graphgen.graph.ResourceNode;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
* The start configuration represents the very start of an infeasible cycle. It contains the cycle's problematic * The abstract base class of the configuration classes related to creating feasible or infeasible MinII-specific
* resource. * cycles. It stores the graph's layer structure, the node table and the cycle's maximum inner delay. The maximum inner
* delay must not be exceeded in order to ensure corresponding MinIIs or other cycle properties.
* *
* @author Sebastian Vollbrecht * @author Sebastian Vollbrecht
*/ */
public class StartConfiguration extends MinIICycleConfiguration { public abstract class MinIICycleConfiguration {
private final Resource problematicResource; protected final LayerStructure layers;
protected final Map<Integer, ResourceNode> nodeTable;
/** /**
* Creates a new start configuration based on the provided arguments. * The cycle's maximum inner delay which must not be exceeded in order to ensure its corresponding MinII (or other
* cycle properties).
*/
protected final int maxInnerDelay;
/**
* Creates a new configuration based on the provided arguments.
*
* @param layers the graph's layer structure
* @param nodeTable the graph's node table
* @param maxInnerDelay the cycle's maximum inner delay
*/
protected MinIICycleConfiguration(LayerStructure layers, Map<Integer, ResourceNode> nodeTable, int maxInnerDelay) {
this.layers = Objects.requireNonNull(layers);
this.nodeTable = Objects.requireNonNull(nodeTable);
if (maxInnerDelay < 0) {
throw new IllegalArgumentException("The maximum inner delay cannot be negative.");
}
this.maxInnerDelay = maxInnerDelay;
}
protected MinIICycleConfiguration(MinIICycleConfiguration other) {
this(other.layers, other.nodeTable, other.maxInnerDelay);
}
/**
* Returns the graph's layer structure.
*
* @return the layer structure
*/
public LayerStructure getLayers() {
return layers;
}
/**
* Returns the graph's mapping of node IDs to nodes.
* *
* @param layers the graph's layer structure * @return the node table
* @param nodeTable the graph's node table
* @param destinationNode the cycle's destination node
* @param destinationNodeLayer the destination node's layer
* @param maxInnerDelay the cycle's maximum inner delay
* @param problematicResource the cycle's problematic resource
*/ */
public StartConfiguration(LayerStructure layers, Map<Integer, ResourceNode> nodeTable, ResourceNode destinationNode, public Map<Integer, ResourceNode> getNodeTable() {
int destinationNodeLayer, int maxInnerDelay, Resource problematicResource) { return nodeTable;
super(layers, nodeTable, destinationNode, destinationNodeLayer, maxInnerDelay);
this.problematicResource = Objects.requireNonNull(problematicResource);
} }
public Resource getProblematicResource() { /**
return problematicResource; * Returns the cycle's inner delay.
*
* @return the cycle's inner delay
*/
public int getMaxInnerDelay() {
return maxInnerDelay;
} }
@Override @Override
...@@ -60,14 +95,13 @@ public class StartConfiguration extends MinIICycleConfiguration { ...@@ -60,14 +95,13 @@ public class StartConfiguration extends MinIICycleConfiguration {
return true; return true;
if (o == null || getClass() != o.getClass()) if (o == null || getClass() != o.getClass())
return false; return false;
if (!super.equals(o)) MinIICycleConfiguration that = (MinIICycleConfiguration) o;
return false; return maxInnerDelay == that.maxInnerDelay && Objects.equals(layers, that.layers) && Objects
StartConfiguration that = (StartConfiguration) o; .equals(nodeTable, that.nodeTable);
return Objects.equals(problematicResource, that.problematicResource);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(super.hashCode(), problematicResource); return Objects.hash(layers, nodeTable, maxInnerDelay);
} }
} }
/*
* 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.minII;
import graphgen.datastructures.Pair;
import graphgen.datastructures.SeededRandom;
import graphgen.generator.components.properties.minII.util.PlannedEdge;
import graphgen.graph.ResourceNode;
import graphgen.util.JavaUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* This is the abstract base class of classes which deal with interconnecting nodes in MinII-specific cycles. Besides
* some small utility methods, it provides methods for determining needed edges (in terms of source-destination pairs)
* and for distributing individual amounts of delay over sets of planned edges. For these purposes it also stores the
* generator's {@link SeededRandom} instance.
*
* @author Sebastian Vollbrecht
*/
public abstract class MinIICycleEdgePlanner {
protected final SeededRandom rng;
protected MinIICycleEdgePlanner(SeededRandom rng) {
this.rng = Objects.requireNonNull(rng);
}
/**
* Computes source-destination node pairs for every destination node which needs an incoming edge.
*
* @return the source-destination node pairs
*/
protected abstract Set<Pair<ResourceNode, ResourceNode>> getIncomingEdgePairs();
/**
* Distributes some internally computed delay over the provided set of edges.
*
* @param edges the set of edges to distribute the delay over
*/
protected abstract void distributeDelay(Set<PlannedEdge> edges);
/**
* Distributes an amount of delay uniformly (for delays approaching infinity) over a collection of planned edges.
*
* @param plannedEdges the collection of planned edges
* @param delay the delay to distribute
* @param rng the {@link SeededRandom} instance to use
* @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, or if the
* delay is negative
*/
protected static void uniformlyDistributeDelay(Collection<PlannedEdge> plannedEdges, int delay, SeededRandom rng) {
if (plannedEdges.isEmpty() && delay > 0) {
throw new IllegalArgumentException("Cannot distribute delay of " + delay + " over non-existent edges.");
} else if (delay < 0) {
throw new IllegalArgumentException("The delay cannot be negative.");
}
while (delay > 0) {
JavaUtils.pickRandomElement(plannedEdges, rng).delay++;
delay--;
}
}
/**
* 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
*/
protected static Map<ResourceNode, PlannedEdge> getIncomingEdgesMap(Set<PlannedEdge> edges,
Collection<ResourceNode> relevantNodes) {
Map<ResourceNode, PlannedEdge> incomingEdgesByNode = new HashMap<>();
for (PlannedEdge edge : Objects.requireNonNull(edges)) {
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.");
}
incomingEdgesByNode.put(edge.dst, edge);
}
}
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
*/
protected static Map<ResourceNode, PlannedEdge> getOutgoingEdgesMap(Set<PlannedEdge> edges,
Collection<ResourceNode> relevantNodes) {
Map<ResourceNode, PlannedEdge> outgoingEdgesByNode = new HashMap<>();
for (PlannedEdge edge : Objects.requireNonNull(edges)) {
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.");
}
outgoingEdgesByNode.put(edge.src, edge);
}
}
return outgoingEdgesByNode;
}
}
...@@ -49,7 +49,7 @@ public abstract class MinIICycleInspector { ...@@ -49,7 +49,7 @@ public abstract class MinIICycleInspector {
* @param backedgeDistance the distance of the backedge(s) which will <i>close</i> the cycle * @param backedgeDistance the distance of the backedge(s) which will <i>close</i> the cycle
* @return the cycle's minimum inner delay adhering to the above-mentioned requirements * @return the cycle's minimum inner delay adhering to the above-mentioned requirements
*/ */
public abstract int getMinInnerDelay(int minII, int backedgeDelay, int backedgeDistance); protected abstract int getMinInnerDelay(int minII, int backedgeDelay, int backedgeDistance);
/** /**
* Returns the maximum inner delay which a cycle induced by the given arguments can have. The implementing classes * Returns the maximum inner delay which a cycle induced by the given arguments can have. The implementing classes
...@@ -62,7 +62,7 @@ public abstract class MinIICycleInspector { ...@@ -62,7 +62,7 @@ public abstract class MinIICycleInspector {
* @param backedgeDistance the distance of the backedge(s) which will <i>close</i> the cycle * @param backedgeDistance the distance of the backedge(s) which will <i>close</i> the cycle
* @return the cycle's maximum inner delay adhering to the above-mentioned requirements * @return the cycle's maximum inner delay adhering to the above-mentioned requirements
*/ */
public abstract int getMaxInnerDelay(int minII, int backedgeDelay, int backedgeDistance); protected abstract int getMaxInnerDelay(int minII, int backedgeDelay, int backedgeDistance);
/** /**
* Returns the minimum inner delay which a cycle containing backedges with the specified parameters can have without * Returns the minimum inner delay which a cycle containing backedges with the specified parameters can have without
...@@ -80,7 +80,7 @@ public abstract class MinIICycleInspector { ...@@ -80,7 +80,7 @@ public abstract class MinIICycleInspector {
* @throws IllegalArgumentException if the MinII is not positive, if the delay is negative or if the distance is not * @throws IllegalArgumentException if the MinII is not positive, if the delay is negative or if the distance is not
* positive * positive
*/ */
public static int minInnerDelay(int minII, int backedgeDelay, int backedgeDistance) { protected static int minInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
if (minII <= 0) { if (minII <= 0) {
throw new IllegalArgumentException("MinII must be positive."); throw new IllegalArgumentException("MinII must be positive.");
} else if (backedgeDelay < 0) { } else if (backedgeDelay < 0) {
...@@ -105,7 +105,7 @@ public abstract class MinIICycleInspector { ...@@ -105,7 +105,7 @@ public abstract class MinIICycleInspector {
* @throws IllegalArgumentException if the MinII is not positive, if the delay is negative or if the distance is not * @throws IllegalArgumentException if the MinII is not positive, if the delay is negative or if the distance is not
* positive * positive
*/ */
public static int maxInnerDelay(int minII, int backedgeDelay, int backedgeDistance) { protected static int maxInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
if (minII <= 0) { if (minII <= 0) {
throw new IllegalArgumentException("MinII must be positive."); throw new IllegalArgumentException("MinII must be positive.");
} else if (backedgeDelay < 0) { } else if (backedgeDelay < 0) {
......
/*
* 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.minII;
import graphgen.datastructures.SeededRandom;
import graphgen.graph.ResourceNode;
import java.util.Objects;
import java.util.Set;
/**
* This is the abstract base class of classes which deal with arranging nodes in MinII-specific cycles. It provides a
* single abstract method for placing nodes in certain target layers. It also stores the generator's {@link
* SeededRandom} instance. This way, the implementing classes can choose the target layer randomly (or other things
* which might be needed).
*
* @author Sebastian Vollbrecht
*/
public abstract class MinIICycleNodePlacer {
protected final SeededRandom rng;
protected MinIICycleNodePlacer(SeededRandom rng) {
this.rng = Objects.requireNonNull(rng);
}
/**
* Places the specified node in one of the possible provided target layers. Depending on the property, this method
* might introduce further changes to the graph (e.g. moving other nodes around).
*
* @param node the node to place in any of the layers
* @param targetLayers the layers the node can be placed in
*/
protected abstract void placeNode(ResourceNode node, Set<Integer> targetLayers);
}
...@@ -33,36 +33,36 @@ import java.util.Set; ...@@ -33,36 +33,36 @@ import java.util.Set;
* *
* @author Sebastian Vollbrecht * @author Sebastian Vollbrecht
*/ */
public class FeasibleCycleInspector extends MinIICycleInspector { class FeasibleCycleInspector extends MinIICycleInspector {
public FeasibleCycleInspector(LayerStructure layers, Set<ResourceNode> nodes) { FeasibleCycleInspector(LayerStructure layers, Set<ResourceNode> nodes) {
super(layers, nodes); super(layers, nodes);
} }
@Override @Override
public int getMinInnerDelay(int minII, int backedgeDelay, int backedgeDistance) { protected int getMinInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
return minInnerDelay(minII, backedgeDelay, backedgeDistance); return minInnerDelay(minII, backedgeDelay, backedgeDistance);
} }
@Override @Override
public int getMaxInnerDelay(int minII, int backedgeDelay, int backedgeDistance) { protected int getMaxInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
return maxInnerDelay(minII, backedgeDelay, backedgeDistance); return maxInnerDelay(minII, backedgeDelay, backedgeDistance);
} }
/** /**
* Computes all destination nodes that could potentially be the destination node for the backedges of a feasible * Computes all initial nodes of a feasible cycle.<p>A node can only be considered initial node if at least one
* cycle.<p>A node can only be considered destination node if at least one other node exists which can be appended * other node exists which can be appended to the initial node without exceeding the specified inner delay.<p>In
* to the destination node without exceeding the specified inner delay.<p>In case of a two-element cycle consisting * case of a two-element cycle consisting of these two nodes only, it is furthermore important to filter out nodes
* of these two nodes only, it is furthermore important to filter out nodes which might result in an * which might result in an
* <i>infeasible</i> cycle if connected to the destination node. For example, if both nodes shared the same * <i>infeasible</i> cycle if connected to the initial node. For example, if both nodes shared the same
* resource with delay 0 and a limit of 1, an edge with delay 0 would render their induced cycle infeasible. * resource with delay 0 and a limit of 1, an edge with delay 0 would render their induced cycle infeasible.
* *
* @param innerDelay the feasible cycle's inner delay * @param innerDelay the feasible cycle's inner delay
* @return a set containing all potential destination node candidates * @return a set containing all potential initial node candidates
*/ */
public Set<ResourceNode> getPossibleDstNodes(int innerDelay) { Set<ResourceNode> getPossibleInitialNodes(int innerDelay) {
Set<ResourceNode> possibleDstNodes = new HashSet<>(); Set<ResourceNode> possibleInitialNodes = new HashSet<>();
Map<Integer, Set<ResourceNode>> nodesByDelay = new HashMap<>(); Map<Integer, Set<ResourceNode>> nodesByDelay = new HashMap<>();
for (ResourceNode node : nodes) { for (ResourceNode node : nodes) {
...@@ -78,9 +78,9 @@ public class FeasibleCycleInspector extends MinIICycleInspector { ...@@ -78,9 +78,9 @@ public class FeasibleCycleInspector extends MinIICycleInspector {
continue; continue;
} }
for (ResourceNode dstNode : firstEntry.getValue()) { for (ResourceNode initialNode : firstEntry.getValue()) {
if (possibleDstNodes.contains(dstNode)) { if (possibleInitialNodes.contains(initialNode)) {
continue; continue;
} }
...@@ -97,22 +97,22 @@ public class FeasibleCycleInspector extends MinIICycleInspector { ...@@ -97,22 +97,22 @@ public class FeasibleCycleInspector extends MinIICycleInspector {
for (ResourceNode otherNode : secondEntry.getValue()) { for (ResourceNode otherNode : secondEntry.getValue()) {
if (dstNode == otherNode) { if (initialNode == otherNode) {
continue; continue;
} }
/* /*
* Infeasibility check. * Infeasibility check.
*/ */
if (dstNode.resource == otherNode.resource) { if (initialNode.resource == otherNode.resource) {
if (dstNode.resource.delay == 0 && dstNode.resource.limit == 1) { if (initialNode.resource.delay == 0 && initialNode.resource.limit == 1) {
/* /*
* If there is no inner delay remaining if these two nodes were used, * If there is no inner delay remaining if these two nodes were used,
* they would need to be connected with an edge with delay 0, thus * they would need to be connected with an edge with delay 0, thus
* rendering the cycle infeasible. * rendering the cycle infeasible.
*/ */
if (innerDelay - (2 * dstNode.resource.delay) == 0) { if (innerDelay - (2 * initialNode.resource.delay) == 0) {
continue; continue;
} }
} }
...@@ -120,11 +120,11 @@ public class FeasibleCycleInspector extends MinIICycleInspector { ...@@ -120,11 +120,11 @@ public class FeasibleCycleInspector extends MinIICycleInspector {
/* /*
* If the first node can be the destination node, the second one can automatically * If the first node can be the destination node, the second one can automatically
* be the destination node as well (skipping them in the next iteration speeds up * be the destination node as well (by skipping them in the next iteration we can
* the calculation). * slightly speed up the calculation).
*/ */
possibleDstNodes.add(dstNode); possibleInitialNodes.add(initialNode);
possibleDstNodes.add(otherNode); possibleInitialNodes.add(otherNode);
break; break;
} }
...@@ -132,7 +132,7 @@ public class FeasibleCycleInspector extends MinIICycleInspector { ...@@ -132,7 +132,7 @@ public class FeasibleCycleInspector extends MinIICycleInspector {
} }
} }
return possibleDstNodes; return possibleInitialNodes;
} }
......
/*
* 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.minII.feasible;
import graphgen.datastructures.Pair;
import graphgen.datastructures.SeededRandom;
import graphgen.generator.components.properties.minII.MinIICycleEdgePlanner;
import graphgen.generator.components.properties.minII.util.PlannedEdge;
import graphgen.graph.ResourceNode;
import graphgen.util.JavaUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* This class is responsible for planning how a feasible arrangements of nodes (as a single path) can be completed with
* edges interconnecting them. It provides methods for retrieving possible incoming edges and for distributing the
* cycles' remaining delays over corresponding planned edges. In other words, it deals with edge delay-specific
* calculations.
*
* @author Sebastian Vollbrecht
*/
class FeasibleEdgePlanner extends MinIICycleEdgePlanner {
private final PathConfiguration pathCfg;
/**
* Creates a new edge planner, based on the provided configurations.
*
* @param pathCfg the path configuration of the feasible cycle
* @param rng the random instance to use to when needed
*/
FeasibleEdgePlanner(PathConfiguration pathCfg, SeededRandom rng) {
super(rng);
this.pathCfg = Objects.requireNonNull(pathCfg);
}
@Override
protected Set<Pair<ResourceNode, ResourceNode>> getIncomingEdgePairs() {
Set<Pair<ResourceNode, ResourceNode>> pairs = new HashSet<>();
List<ResourceNode> path = pathCfg.getPath();
for (int i = 0; i < path.size() - 1; i++) {
pairs.add(Pair.of(path.get(i), path.get(i + 1)));
}
return pairs;
}
/**
* Distributes the cycle's remaining inner delay over the provided set of planned edges. This method makes sure that
* no infeasible timeslot will be created, which could happen when too many resource-sharing nodes with a delay of
* zero were placed in the same timeslot.
*
* @param edges the existing set of planned edges
*/
@Override
protected void distributeDelay(Set<PlannedEdge> edges) {
List<PlannedEdge> edgePath = new ArrayList<>();
Map<ResourceNode, PlannedEdge> incomingEdgesByNode = getIncomingEdgesMap(edges, pathCfg.getPath());
pathCfg.getPath().stream().map(incomingEdgesByNode::get).filter(Objects::nonNull).forEach(edgePath::add);
int nodesSharingSameResource = 1;
List<PlannedEdge> criticalEdges = new ArrayList<>();
for (int i = 0; i < edgePath.size(); i++) {
PlannedEdge edge = edgePath.get(i);
ResourceNode src = edge.src;
ResourceNode dst = edge.dst;
boolean isSrcResourceCritical = src.resource.delay == 0 && src.isLimited();
boolean isEdgeCritical = isSrcResourceCritical && src.resource == dst.resource && edge.delay == 0;
if (isEdgeCritical) {
nodesSharingSameResource++;
criticalEdges.add(edge);
}
if (!isEdgeCritical || i == edgePath.size() - 1) {
if (!criticalEdges.isEmpty() && nodesSharingSameResource > src.resource.limit) {
makeSubPathFeasible(criticalEdges, src.resource.limit);
}
nodesSharingSameResource = 1;
criticalEdges.clear();
}
}
int remainingInnerDelay = pathCfg.getRemainingInnerDelay() - edges.stream().mapToInt(e -> e.delay).sum();
uniformlyDistributeDelay(edges, remainingInnerDelay, rng);
}
private void makeSubPathFeasible(List<PlannedEdge> subPath, int limit) {
int nodeCount = subPath.size() + 1;
int timeslotsNeeded = (int) Math.ceil((double) nodeCount / limit);