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 @@
* 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.Resource;
import graphgen.graph.ResourceNode;
import java.util.Map;
import java.util.Objects;
/**
* The start configuration represents the very start of an infeasible cycle. It contains the cycle's problematic
* resource.
* The abstract base class of the configuration classes related to creating feasible or infeasible MinII-specific
* 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
*/
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
* @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
* @return the node table
*/
public StartConfiguration(LayerStructure layers, Map<Integer, ResourceNode> nodeTable, ResourceNode destinationNode,
int destinationNodeLayer, int maxInnerDelay, Resource problematicResource) {
super(layers, nodeTable, destinationNode, destinationNodeLayer, maxInnerDelay);
this.problematicResource = Objects.requireNonNull(problematicResource);
public Map<Integer, ResourceNode> getNodeTable() {
return nodeTable;
}
public Resource getProblematicResource() {
return problematicResource;
/**
* Returns the cycle's inner delay.
*
* @return the cycle's inner delay
*/
public int getMaxInnerDelay() {
return maxInnerDelay;
}
@Override
......@@ -60,14 +95,13 @@ public class StartConfiguration extends MinIICycleConfiguration {
return true;
if (o == null || getClass() != o.getClass())
return false;
if (!super.equals(o))
return false;
StartConfiguration that = (StartConfiguration) o;
return Objects.equals(problematicResource, that.problematicResource);
MinIICycleConfiguration that = (MinIICycleConfiguration) o;
return maxInnerDelay == that.maxInnerDelay && Objects.equals(layers, that.layers) && Objects
.equals(nodeTable, that.nodeTable);
}
@Override
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 {
* @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
*/
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
......@@ -62,7 +62,7 @@ public abstract class MinIICycleInspector {
* @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
*/
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
......@@ -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
* positive
*/
public static int minInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
protected static int minInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
if (minII <= 0) {
throw new IllegalArgumentException("MinII must be positive.");
} else if (backedgeDelay < 0) {
......@@ -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
* positive
*/
public static int maxInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
protected static int maxInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
if (minII <= 0) {
throw new IllegalArgumentException("MinII must be positive.");
} 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;
*
* @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);
}
@Override
public int getMinInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
protected int getMinInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
return minInnerDelay(minII, backedgeDelay, backedgeDistance);
}
@Override
public int getMaxInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
protected int getMaxInnerDelay(int minII, int backedgeDelay, int backedgeDistance) {
return maxInnerDelay(minII, backedgeDelay, backedgeDistance);
}
/**
* Computes all destination nodes that could potentially be the destination node for the backedges of a feasible
* cycle.<p>A node can only be considered destination node if at least one other node exists which can be appended
* to the destination node without exceeding the specified inner delay.<p>In case of a two-element cycle consisting
* of these two nodes only, it is furthermore important to filter out nodes which might result in an
* <i>infeasible</i> cycle if connected to the destination node. For example, if both nodes shared the same
* Computes all initial nodes of a feasible cycle.<p>A node can only be considered initial node if at least one
* other node exists which can be appended to the initial node without exceeding the specified inner delay.<p>In
* case of a two-element cycle consisting of these two nodes only, it is furthermore important to filter out nodes
* which might result in an
* <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.
*
* @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<>();
for (ResourceNode node : nodes) {
......@@ -78,9 +78,9 @@ public class FeasibleCycleInspector extends MinIICycleInspector {
continue;
}
for (ResourceNode dstNode : firstEntry.getValue()) {
for (ResourceNode initialNode : firstEntry.getValue()) {
if (possibleDstNodes.contains(dstNode)) {
if (possibleInitialNodes.contains(initialNode)) {
continue;
}
......@@ -97,22 +97,22 @@ public class FeasibleCycleInspector extends MinIICycleInspector {
for (ResourceNode otherNode : secondEntry.getValue()) {
if (dstNode == otherNode) {
if (initialNode == otherNode) {
continue;
}
/*
* Infeasibility check.
*/
if (dstNode.resource == otherNode.resource) {
if (dstNode.resource.delay == 0 && dstNode.resource.limit == 1) {
if (initialNode.resource == otherNode.resource) {
if (initialNode.resource.delay == 0 && initialNode.resource.limit == 1) {
/*
* 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
* rendering the cycle infeasible.
*/
if (innerDelay - (2 * dstNode.resource.delay) == 0) {
if (innerDelay - (2 * initialNode.resource.delay) == 0) {
continue;
}
}
......@@ -120,11 +120,11 @@ public class FeasibleCycleInspector extends MinIICycleInspector {
/*
* 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
* the calculation).
* be the destination node as well (by skipping them in the next iteration we can
* slightly speed up the calculation).
*/
possibleDstNodes.add(dstNode);
possibleDstNodes.add(otherNode);
possibleInitialNodes.add(initialNode);
possibleInitialNodes.add(otherNode);
break;
}
......@@ -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);
int[] nodesPerTimeslot = new int[timeslotsNeeded];
while (nodeCount > 0) {
Set<Integer> candidates = new HashSet<>();
for (int i = 0; i < nodesPerTimeslot.length; i++) {
if (nodesPerTimeslot[i] < limit) {
candidates.add(i);
}
}
nodesPerTimeslot[JavaUtils.pickRandomElement(candidates, rng)]++;
nodeCount--;
}
for (int i = 0; i < nodesPerTimeslot.length - 1; i++) {
int edgeIndex = nodesPerTimeslot[i];
for (int j = 0; j < i; j++) {
edgeIndex += nodesPerTimeslot[j];
}
subPath.get(edgeIndex - 1).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.minII.feasible;
import graphgen.datastructures.Pair;
import graphgen.datastructures.SeededRandom;
import graphgen.generator.components.properties.minII.MinIICycleNodePlacer;
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;
/**
* This class is responsible for arranging nodes in a feasible fashion. It provides methods for determining a
* <i>feasible path's</i> possible preceding or succeeding nodes and also a method for placing them in their
* corresponding layers.
*
* @author Sebastian Vollbrecht
*/
class FeasibleNodePlacer extends MinIICycleNodePlacer {
private final PathConfiguration pathCfg;
/**
* Creates a new node placer, based on the provided path configuration.
*
* @param pathCfg the path configuration of the feasible cycle
* @param rng the random instance to use to when needed
*/
FeasibleNodePlacer(PathConfiguration pathCfg, SeededRandom rng) {
super(rng);
this.pathCfg = Objects.requireNonNull(pathCfg);
}