Commit 5ccf5525 authored by Sebastian Vollbrecht's avatar Sebastian Vollbrecht
Browse files

Began working on an InfeasibleMinII-FeasibleII property (for fixed numbers of...

Began working on an InfeasibleMinII-FeasibleII property (for fixed numbers of infeasible II candidates).
parent 89e2b3ee
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
/*
 * 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.combined;

import graphgen.enums.ModuloSchedulingFormulation;
import graphgen.generator.components.properties.minII.infeasible.InfeasibleMinIICycleProperty;
import graphgen.graph.ResourceNode;
import graphgen.util.SchedulingUtils;
import modsched.Edge;

import java.util.HashSet;
import java.util.Set;

/**
 * This property can be used to define the distance of the first <i>feasible</i> II to a defined MinII.
 *
 * @author Sebastian Vollbrecht
 */
public class InfeasibleMinIIFeasibleIIProperty extends InfeasibleMinIICycleProperty {

	private final ModuloSchedulingFormulation formulation;

	/**
	 * Creates a new infeasible MinII & feasible II property which ensures that the specified MinII and the number of
	 * subsequent given timesteps will be (and remain) infeasible during graph creation. The delay and distance
	 * parameters will be used for the RecMinII-ensuring backedges. The backedge parameters are set to 1 in this
	 * constructor. The infeasible timesteps parameter denotes how many timesteps should be infeasible, starting at the
	 * MinII's timestep (inclusive).
	 *
	 * @param minII               the MinII to ensure
	 * @param infeasibleTimesteps the number of infeasible timesteps
	 * @param formulation         the ILP formulation to use to determine edge and backedge validity for the (MinII +
	 *                            infeasible timesteps)-th timestep
	 * @throws IllegalArgumentException if the MinII is not greater than zero, if the backedge delay is less than zero
	 *                                  or if the backedge distance is not greater than zero
	 */
	public InfeasibleMinIIFeasibleIIProperty(int minII, int infeasibleTimesteps,
											 ModuloSchedulingFormulation formulation) {
		this(minII, 1, 1, infeasibleTimesteps, formulation);
	}

	/**
	 * Creates a new infeasible MinII & feasible II property which ensures that the specified MinII and the number of
	 * given timesteps will be (and remain) infeasible during graph creation. The delay and distance parameters will be
	 * used for the RecMinII-ensuring backedges. The infeasible timesteps parameter denotes how many timesteps should be
	 * infeasible, starting at the MinII's timestep (inclusive).
	 *
	 * @param minII               the MinII to ensure
	 * @param backedgeDelay       the delay of the RecMinII-ensuring backedges
	 * @param backedgeDistance    the distance of the RecMinII-ensuring backedges
	 * @param infeasibleTimesteps the number of infeasible timesteps
	 * @param formulation         the ILP formulation to use to determine edge and backedge validity for the (MinII +
	 *                            infeasible timesteps)-th timestep
	 * @throws IllegalArgumentException if the MinII is not greater than zero, if the backedge delay is less than zero
	 *                                  or if the backedge distance is not greater than zero
	 */
	public InfeasibleMinIIFeasibleIIProperty(int minII, int backedgeDelay, int backedgeDistance,
											 int infeasibleTimesteps, ModuloSchedulingFormulation formulation) {
		super(minII, backedgeDelay, backedgeDistance, infeasibleTimesteps);
		this.formulation = formulation;
	}

	@Override
	public boolean isEdgeValid(ResourceNode src, ResourceNode dst, int delay) {
		return isBackedgeValid(src, dst, delay, 0);
	}

	@Override
	public boolean isBackedgeValid(ResourceNode src, ResourceNode dst, int delay, int distance) {

		Set<Edge<ResourceNode>> tmpEdges = new HashSet<>(edgeCreator.edgeView());
		tmpEdges.add(new Edge<>(src, dst, delay, distance));

		if (SchedulingUtils.getRecMinII(nodes, tmpEdges) > minII) {
			return false;
		}

		int firstFeasibleII = minII + infeasibleIIs;

		for (int II = minII; II < firstFeasibleII; II++) {
			assert !SchedulingUtils.isFeasible(nodes, tmpEdges, formulation, II, II);
		}

		return SchedulingUtils.schedule(nodes, tmpEdges, "", formulation, 1, minII, firstFeasibleII)
				.getII() == firstFeasibleII;

	}
}
+366 −0
Original line number Diff line number Diff line
/*
 * 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.infeasible;

import graphgen.datastructures.Pair;
import graphgen.generator.components.properties.minII.MinIIProperty;
import graphgen.generator.components.properties.minII.util.PlannedEdge;
import graphgen.graph.Resource;
import graphgen.graph.ResourceNode;
import graphgen.util.JavaUtils;
import modsched.Edge;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Predicate;

/**
 * TODO: place your javadoc here
 *
 * @author Sebastian Vollbrecht
 */
public abstract class InfeasibleMinIICycleProperty extends MinIIProperty<InfeasibleConfiguration> {

	/**
	 * Stores how many IIs should be infeasible in total, starting at the cycle's MinII (inclusive).
	 */
	protected final int infeasibleIIs;

	protected InfeasibleMinIICycleProperty(int minII, int backedgeDelay, int backedgeDistance, int infeasibleIIs) {
		super(minII, backedgeDelay, backedgeDistance);
		this.infeasibleIIs = JavaUtils.requireGreater(infeasibleIIs, 0);
	}

	@Override
	protected boolean isResMinIISufficient(int resMinII) {
		/*
		 * We *have* to create an infeasible sub graph - even if the ResMinII equals the MinII -
		 * as the generated graph itself might not be infeasible in all cases.
		 */
		return false;
	}

	@Override
	protected int getMinInnerDelay() {
		/*
		 * We must use the absolute maximum inner delay for infeasible cycles.
		 * Otherwise we might introduce slack, resulting in potentially feasible cycles.
		 */
		return maxInnerDelay(minII, backedgeDelay, backedgeDistance);
	}

	@Override
	protected int getMaxInnerDelay() {
		return maxInnerDelay(minII, backedgeDelay, backedgeDistance);
	}

	@Override
	protected Set<InfeasibleConfiguration> getInitialConfigurations(int maxInnerDelay) {

		/* Determine possible problematic resources from the given set of nodes. */
		Set<Resource> problematicResources = getProblematicResources(maxInnerDelay);

		/*
		 * Possible infeasible configurations can be computed for every potential resource.
		 */
		Map<Resource, Set<InfeasibleConfiguration>> configurationsByResource = new HashMap<>();

		for (Resource problematicResource : problematicResources) {

			Set<InfeasibleConfiguration> infeasibleConfigurations = new HashSet<>();

			for (int dstNodeLayer = 0; dstNodeLayer < layers.getHeight() - 1; dstNodeLayer++) {

				InfeasibleCycleInspector inspector = new InfeasibleCycleInspector(layers, nodes, problematicResource,
																				  infeasibleIIs, dstNodeLayer
				);

				Set<ResourceNode> possibleDstNodes = inspector.getInitialNodes(maxInnerDelay);

				/*
				 * Continue with the next destination node layer if there are no valid
				 * destination nodes for the current destination node layer.
				 */
				if (possibleDstNodes.isEmpty()) {
					continue;
				}

				/*
				 * If any of the currently possible destination nodes is already contained in
				 * the current layer, we prefer choosing one of them. Otherwise, randomly choose
				 * a possible destination node.
				 */
				Set<Integer> currentLayer = layers.getLayer(dstNodeLayer);
				Set<ResourceNode> nodesInCurrentLayer = new HashSet<>();
				possibleDstNodes.stream().filter(n -> currentLayer.contains(n.getId()))
						.forEach(nodesInCurrentLayer::add);

				ResourceNode dstNode;

				if (!nodesInCurrentLayer.isEmpty()) {
					dstNode = JavaUtils.pickRandomElement(nodesInCurrentLayer, rng);
				} else {
					dstNode = JavaUtils.pickRandomElement(possibleDstNodes, rng);
				}

				infeasibleConfigurations
						.add(new InfeasibleConfiguration(layers, nodeTable, maxInnerDelay, problematicResource,
														 infeasibleIIs, dstNode, dstNodeLayer
						));

			}

			if (!infeasibleConfigurations.isEmpty()) {
				configurationsByResource.put(problematicResource, infeasibleConfigurations);
			}

		}

		if (configurationsByResource.isEmpty()) {
			return Collections.emptySet();
		}

		/*
		 * Randomly choose from the remaining resources a potential resource and return the
		 * set of corresponding infeasible configurations.
		 */
		Resource problematicResource = JavaUtils.pickRandomElement(problematicResources, rng);
		return configurationsByResource.get(problematicResource);
	}

	/**
	 * Computes possible problematic resources whose limit could be exceeded in a cycle. 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 corresponding MinIIs.
	 *
	 * @param maxInnerDelay the cycle's maximum inner delay
	 * @return a set containing problematic resource candidates
	 */
	private Set<Resource> getProblematicResources(int maxInnerDelay) {

		Set<Resource> potentialResources = new HashSet<>();

		/*
		 * Maps limited resources to the amount of nodes using them.
		 */
		Map<Resource, Integer> limitedResourceUsages = new HashMap<>();

		for (ResourceNode node : nodes) {

			if (node.isUnlimited()) {
				continue;
			}

			JavaUtils.insertOrIncValue(limitedResourceUsages, node.resource);

		}

		/*
		 * A resource is only a valid problematic resource if it is used more than its
		 * limit allows for throughout the graph and if its delay does not exceed the
		 * maximum possible inner delay.
		 */
		for (Map.Entry<Resource, Integer> resourceUsage : limitedResourceUsages.entrySet()) {

			Resource resource = resourceUsage.getKey();

			if (resourceUsage.getValue() > (resource.limit * infeasibleIIs) && resource.delay <= maxInnerDelay) {
				potentialResources.add(resource);
			}
		}

		return potentialResources;

	}

	@Override
	protected void arrangeNodes(int innerDelay, InfeasibleConfiguration infeasibleCfg) {

		Resource problematicResource = infeasibleCfg.getProblematicResource();

		/*
		 * If and only if the following conditions are met, the destination node can be
		 * counted as a problematic node, since it then won't shift the subsequent
		 * layers' time slots (where other problematic nodes might need to be placed).
		 */
		if (infeasibleCfg.getDestinationNode().resource.equals(problematicResource) && problematicResource.delay == 0) {
			infeasibleCfg.addProblematicNode(infeasibleCfg.getDestinationNode());
		}

		InfeasibleNodePlacer placer = new InfeasibleNodePlacer(infeasibleCfg, rng);

		placer.placeNode(infeasibleCfg.getDestinationNode(), JavaUtils.asSet(infeasibleCfg.getDestinationNodeLayer()));

		/*
		 * Gather all remaining nodes which use the problematic resource and randomly
		 * choose the required amount of nodes to create the infeasible cycle.
		 */
		Set<ResourceNode> problematicCandidates = new HashSet<>();
		for (ResourceNode node : nodes) {
			if (node.resource.equals(problematicResource) && !node.equals(infeasibleCfg.getDestinationNode())) {
				problematicCandidates.add(node);
			}
		}

		for (int i = 0; i < infeasibleCfg.howManyLeftToPlace(); i++) {
			ResourceNode problematicNode = JavaUtils.removeRandomElement(problematicCandidates, rng);
			infeasibleCfg.addProblematicNode(problematicNode);
		}

		/*
		 * Now we place the remaining problematic nodes. By sorting them first
		 * using their layer depths we can achieve that most of the problematic nodes
		 * will be placed in the layers they are currently residing in. Otherwise they
		 * could be replaced by zero nodes if a lower problematic node required one to
		 * be placed in the respective layer.
		 */
		for (ResourceNode problematicNode : JavaUtils
				.asSortedList(infeasibleCfg.getProblematicNodes(), Comparator.comparingInt(layers::getDepth))) {

			/*
			 * If a problematic node has been fixed already, we don't need to place it
			 * anymore. This can only occur if the problematic resource's delay is zero and
			 * a problematic node has been chosen as a zero node while moving across layer
			 * boundaries.
			 */
			if (!infeasibleCfg.getFixedNodes().contains(problematicNode)) {
				Set<Integer> possibleLayers = placer.getPossibleLayers(problematicNode);
				placer.placeNode(problematicNode, possibleLayers);
			}

		}

		/*
		 * Now try to append a suffix to the infeasible part of the cycle.
		 */
		int remainingInnerDelay = infeasibleCfg.getRemainingInnerDelay();

		/*
		 * 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();
		int maxDepth = Collections.max(infeasibleCfg.getProblematicNodeCountsByLayer().keySet());

		for (int layer = maxDepth + 1; layer < layers.getHeight(); layer++) {

			Set<ResourceNode> candidates = new HashSet<>();

			if (remainingInnerDelay == 0 && rng.nextDouble() > pToAppendMoreZeroNodes) {
				break;
			}

			for (int nodeID : layers.getLayer(layer)) {

				ResourceNode candidate = nodeTable.get(nodeID);

				if (candidate.getDelay() <= remainingInnerDelay) {
					candidates.add(candidate);
				}
			}

			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);
			infeasibleCfg.appendSuffixNode(candidate);
			remainingInnerDelay -= candidate.getDelay();

		}

	}

	@Override
	public void reset() {
		// Do nothing.
	}

	@Override
	public void notify(Edge<ResourceNode> edge) {
		// Do nothing.
	}

	@Override
	protected Set<PlannedEdge> getNeededEdges(InfeasibleConfiguration infeasibleCfg,
											  BiFunction<ResourceNode, ResourceNode, Integer> delayComputer) {

		Set<PlannedEdge> plannedEdges = new HashSet<>();

		/* Choose the timeslot of the problematic nodes (relative to the destination node). */
		Set<Integer> possibleTimeslots = InfeasibleEdgePlanner.getProblematicTimeslots(infeasibleCfg);
		int problematicTimeslot = JavaUtils.pickRandomElement(possibleTimeslots, rng);

		/*
		 * We need to create an incoming edge for all fixed nodes. The source node can
		 * either be the destination node or a zero node of the previous layer.
		 * Also create an incoming edge for every suffix node.
		 */
		InfeasibleEdgePlanner edgePlanner = new InfeasibleEdgePlanner(infeasibleCfg, problematicTimeslot, rng);

		/* Stores pairs of source and destination nodes which need to be connected with an edge. */
		Set<Pair<ResourceNode, ResourceNode>> edgePairs = edgePlanner.getNeededEdgePairs();
		edgePairs.forEach(p -> plannedEdges.add(new PlannedEdge(p.first, p.second, 0)));

		/*
		 * Update the delay of the created incoming edges so that the problematic nodes will share
		 * the chosen timeslot. The remaining inner delay will also be distributed over the
		 * non-problematic edges (e.g. the suffix edges).
		 */
		edgePlanner.distributeDelay(plannedEdges, delayComputer);

		/*
		 * Create the required outgoing edges.
		 * Note that there is no need to create edges for problematic nodes which have
		 * been connected to other nodes already (this can happen if they are zero
		 * nodes themselves).
		 */
		Predicate<ResourceNode> needsOutgoingEdge = n -> plannedEdges.stream().noneMatch(e -> e.src.equals(n));
		Set<PlannedEdge> outgoingEdges = edgePlanner
				.getOutgoingEdges(problematicTimeslot, needsOutgoingEdge, delayComputer);
		plannedEdges.addAll(outgoingEdges);

		/*
		 * Finally, add the backedges going to the destination node. The backedge
		 * source selection depends on whether or not a suffix exists.
		 */
		ResourceNode dst = infeasibleCfg.getDestinationNode();
		if (infeasibleCfg.getSuffixNodes().isEmpty()) {
			for (ResourceNode src : infeasibleCfg.getBackedgeSources()) {
				plannedEdges.add(new PlannedEdge(src, dst, backedgeDelay, backedgeDistance));
			}
		} else {
			ResourceNode src = infeasibleCfg.getSuffixNodes().get(infeasibleCfg.getSuffixNodes().size() - 1);
			plannedEdges.add(new PlannedEdge(src, dst, backedgeDelay, backedgeDistance));
		}

		return plannedEdges;

	}
}
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ public class Main {

		// CaseStudyCASES2018.generate();
		// RandomGraphs.generate();

	}

	/**
+41 −9
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Callable;
@@ -294,8 +295,9 @@ public class SchedulingUtils {

			for (Edge<ResourceNode> edge : incoming.get(node)) {

				if (edge.isBackedge())
				if (edge.isBackedge()) {
					continue;
				}

				ResourceNode src = edge.getSrc();

@@ -319,16 +321,46 @@ public class SchedulingUtils {

		}

		int maxII = 0;
		for (Entry<ResourceNode, Integer> timeEntry : earliestPossibleTimes.entrySet()) {
			/*
			 * The additional call to Math.max(...) makes sure that 'end' nodes with delay
			 * 0 don't lead to the next iteration beginning in the end node's timeslot.
			 */
			maxII = Math.max(timeEntry.getValue() + Math.max(timeEntry.getKey().getDelay(), 1), maxII);
		}

		/*
		 * Check whether the current MaxII fulfills every single backedge dependency by
		 * calculating how far away the backedge's source and destination nodes would be
		 * placed in the non-modulo schedule. If they are too close together, the MaxII
		 * needs to be increased accordingly.
		 */
		for (Edge<ResourceNode> backedge : JavaUtils
				.asSet(graph.edges.stream().filter(EdgeType.BACKWARD::matchesEdge))) {

			ResourceNode src = backedge.getSrc();
			ResourceNode dst = backedge.getDst();

			int srcTime = earliestPossibleTimes.get(src);
			int dstTime = earliestPossibleTimes.get(dst);

			/*
		 * Because the computation of the MaxII does not take into account the
		 * inter-iteration-dependencies imposed by the backedges, it might be smaller
		 * than the RecMinII of the graph. Therefore, the greater value of both II
		 * bounds should be returned.
			 * The resulting time denotes the time of the destination node in the next
			 * iteration.
			 */
		int maxII = layers.getLastLayer().stream().map(graph::getNode)
				.mapToInt(n -> earliestPossibleTimes.get(n) + n.getDelay()).max().orElse(0);
			int resultingTimeDelta = (dstTime + maxII) - srcTime;
			int neededTimeDelta = (src.getDelay() + backedge.getDelay()) / backedge.getDistance();

			int delta = neededTimeDelta - resultingTimeDelta;

			if (delta > 0) {
				maxII += delta;
			}

		}

		return Math.max(maxII, getMinII(graph));
		return maxII;

	}

@@ -369,7 +401,7 @@ public class SchedulingUtils {
			}
		}

		resourceUsagePerSlot.get(nodeSlot).put(res, resourceUsagePerSlot.get(nodeSlot).get(res) + 1);
		JavaUtils.insertOrIncValue(resourceUsagePerSlot.get(nodeSlot), res);
		nodeTimes.put(node, nodeSlot);
	}

+39 −0
Original line number Diff line number Diff line
@@ -554,6 +554,45 @@ public class SchedulingUtilsTest {
		assertEquals(13, SchedulingUtils.getMaxII(graph));
	}

	@Test
	public void testMaxII2() {

		Resource limited = new Resource("adder", 1, 1);
		Resource unlimited = new Resource("unlimited", 0);

		ResourceNode n0 = new ResourceNode(0, unlimited);
		ResourceNode n1 = new ResourceNode(1, unlimited);
		ResourceNode n2 = new ResourceNode(2, limited);
		ResourceNode n3 = new ResourceNode(3, limited);
		ResourceNode n4 = new ResourceNode(4, limited);
		ResourceNode n5 = new ResourceNode(5, unlimited);

		Set<ResourceNode> nodes = JavaUtils.asSet(n0, n1, n2, n3, n4, n5);
		Set<Edge<ResourceNode>> edges = new HashSet<>();

		edges.add(new Edge<>(n0, n1, 0, 0));
		edges.add(new Edge<>(n0, n2, 0, 0));
		edges.add(new Edge<>(n0, n3, 0, 0));
		edges.add(new Edge<>(n1, n4, 0, 0));
		edges.add(new Edge<>(n2, n5, 1, 0));
		edges.add(new Edge<>(n3, n5, 1, 0));
		edges.add(new Edge<>(n4, n5, 1, 0));

		Edge<ResourceNode> edge = new Edge<>(n5, n0, 1, 1);
		edges.add(edge);
		//assertEquals(5, SchedulingUtils.getMaxII(new Graph<>(nodes, edges)));
		edges.remove(edge);

		edge = new Edge<>(n5, n0, 50, 1);
		edges.add(edge);
		assertEquals(54, SchedulingUtils.getMaxII(new Graph<>(nodes, edges)));
		edges.remove(edge);

		edge = new Edge<>(n5, n0, 50, 2);
		edges.add(edge);
		assertEquals(29, SchedulingUtils.getMaxII(new Graph<>(nodes, edges)));
	}

	@Test
	public void minIIGreaterMaxIIProblematicOne1() {