Newer
Older
AlgebraicDataflowArchitectureModel / AlgebraicDataflowArchitectureModel / src / algorithms / DataTransferModelAnalyzer.java
package algorithms;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import models.*;
import models.algebra.*;
import models.dataConstraintModel.*;
import models.dataFlowModel.*;

/**
 * Algorithms to analyze data transfer model.
 * 
 * @author Nitta
 *
 */
public class DataTransferModelAnalyzer {
	/**
	 * Create data flow graph annotated with node attributes that indicate whether each resource state needs to be stored.
	 * @param model a data transfer model
	 * @return annotated data flow graph
	 */
	static public DataFlowGraph createDataFlowGraphWithStateStoringAttribute(DataTransferModel model) {
		DataFlowGraph graph = model.getDataFlowGraph();
		Collection<Channel> channels = new HashSet<>(model.getInputChannels());
		channels.addAll(model.getChannels());
		for (Channel channel: channels) {
			for (ChannelMember member: ((DataTransferChannel) channel).getOutputChannelMembers()) {
				boolean toBeStored = !member.getStateTransition().isRightUnary();		// The state does not need to be stored if the state transition function is right unary.
				for (Node node : graph.getResourceNodes()) {
					if (((ResourceNode) node).getInSideResources().contains(member.getResource())) {
						setStoreAttribute((ResourceNode) node, toBeStored);
					}
				}
			}
		}
		for (Node node: graph.getResourceNodes()) {
			HashSet<Channel> inChannels = new HashSet<>();
			for(Edge inEdge: ((ResourceNode) node).getInEdges()) {
				if (inEdge instanceof DataFlowEdge) {
					DataFlowEdge dfEdge = (DataFlowEdge) inEdge;
					if (dfEdge.isChannelToResource()) {
						inChannels.add(((ChannelNode) dfEdge.getSource()).getChannel());
					}
				}
			}
			if ((inChannels.size() > 1)) {
				// If the resource has multiple input channels, then the state of the resource needs to be stored.
				setStoreAttribute((ResourceNode) node, true);
			} else if (((ResourceNode) node).getAttribute() == null) {
				setStoreAttribute((ResourceNode) node, false);
			}
		}
		return graph;
	}

	static private void setStoreAttribute(ResourceNode node, boolean toBeStored) {
		NodeAttribute attr = node.getAttribute();
		StoreAttribute store;
		if (attr != null && attr instanceof NodeAttribute) {
			store = (StoreAttribute) attr;
			store.setNeeded(store.isNeeded() || toBeStored);
		} else {
			store = new StoreAttribute();
			store.setNeeded(toBeStored);
			node.setAttribute(store);
		}
	}

	/**
	 * Annotate data flow graph with edge attributes that indicate selectable data transfer methods.
	 * @param graph a data flow graph
	 * @return annotated data flow graph
	 */
	static public DataFlowGraph annotateWithSelectableDataTransferAttiribute(DataFlowGraph graph) {
		HashSet<Node> unvisitedNodes = new HashSet<>(graph.getResourceNodes());
		// Turn push only
		for (Node resNode: graph.getResourceNodes()) {
			if (unvisitedNodes.contains(resNode) && ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isNeeded()) {
				unvisitedNodes.remove(resNode);
				trackEdgesBackwardForPush(resNode, unvisitedNodes);
			}
		}
		// Turn push/pull only with respect to channel hierarchies.
		while (!unvisitedNodes.isEmpty()) {
			Node resNode = unvisitedNodes.iterator().next();
			unvisitedNodes.remove(resNode);
			for (Edge chToRes : ((ResourceNode) resNode).getInEdges()) {
				ChannelNode chNode = (ChannelNode) chToRes.getSource();
				// Should take into account the channel hierarchy.
				boolean pullContained = false;
				Set<ChannelNode> ancestorChannels = chNode.getAncestors();
				for (ChannelNode ancestorCh: ancestorChannels) {
					for (Edge resToCh: ancestorCh.getInEdges()) {
						ResourceNode srcResNode = (ResourceNode) resToCh.getSource();
						DataTransferChannel ch = (DataTransferChannel) ancestorCh.getChannel();
						for (ChannelMember cm: ch.getInputChannelMembers()) {
							if (cm.isOutside()) {
								PushPullAttribute ppat = new PushPullAttribute();
								ppat.addOption(PushPullValue.PULL);		// To refer to outside resource.
								((DataFlowEdge) resToCh).setAttribute(ppat);
								pullContained = true;
							} else {
								PushPullAttribute ppat = new PushPullAttribute();
								ppat.addOption(PushPullValue.PUSH);		// For broadcasting transfer.
								((DataFlowEdge) resToCh).setAttribute(ppat);
								unvisitedNodes.remove(srcResNode);
								trackEdgesBackwardForPush(srcResNode, unvisitedNodes);
							}
						}
					}
				}
				Set<ChannelNode> descendantChannels = chNode.getDescendants();
				for (ChannelNode descendantCh: descendantChannels) {
					for (Edge resToCh: descendantCh.getInEdges()) {
						PushPullAttribute ppat = new PushPullAttribute();
						ppat.addOption(PushPullValue.PULL);		// For collecting transfer.
						((DataFlowEdge) resToCh).setAttribute(ppat);
						pullContained = true;
					}
				}
				if (pullContained) {
					trackEdgesForwardForPull(resNode, unvisitedNodes);
				}
			}
		}
		// set push/pull attributes to the remaining edges
		for (Edge e : graph.getEdges()) {
			if (!((DataFlowEdge) e).isChannelToResource() && ((DataFlowEdge) e).getAttribute() == null) {
				PushPullAttribute ppat = new PushPullAttribute();
				ppat.addOption(PushPullValue.PUSHorPULL);
				ppat.addOption(PushPullValue.PUSH);
				ppat.addOption(PushPullValue.PULL);
				((DataFlowEdge) e).setAttribute(ppat);
			}
		}
		return graph;
	}

	static private void trackEdgesBackwardForPush(Node resNode, HashSet<Node> unvisitedNodes) {
		// recursively turn push only backward in data-flow.
		for (Edge chToRes : ((ResourceNode) resNode).getInEdges()) {
			ChannelNode chNode = (ChannelNode) chToRes.getSource();
			// Should take into account the channel hierarchy.
			Set<ChannelNode> ancestorChannels = chNode.getAncestors();
			Set<ChannelNode> descendantChannels = chNode.getDescendants();
			Set<Edge> inEdges = new HashSet<>();
			inEdges.addAll(chNode.getInEdges());
			for (ChannelNode ancestorCh: ancestorChannels) {
				inEdges.addAll(ancestorCh.getInEdges());
			}
			for (ChannelNode descendantCh: descendantChannels) {
				inEdges.addAll(descendantCh.getInEdges());
			}
			for (Edge resToCh: inEdges) {
				PushPullAttribute ppat = new PushPullAttribute();
				ppat.addOption(PushPullValue.PUSH);
				((DataFlowEdge) resToCh).setAttribute(ppat);
				Node resNode2 = resToCh.getSource();
				if (unvisitedNodes.contains(resNode2)) {
					unvisitedNodes.remove(resNode2);
					trackEdgesBackwardForPush(resNode2, unvisitedNodes);
				}
			}
		}
	}

	private static void trackEdgesForwardForPull(Node resNode, HashSet<Node> unvisitedNodes) {
		// recursively turn pull only forward in data-flow.
		for (Edge resToCh : ((ResourceNode) resNode).getOutEdges()) {
			PushPullAttribute ppat = new PushPullAttribute();
			ppat.addOption(PushPullValue.PULL);
			((DataFlowEdge) resToCh).setAttribute(ppat);
			ChannelNode chNode = (ChannelNode) resToCh.getDestination();
			// Should take into account the channel hierarchy.
			Set<ChannelNode> ancestorChannels = chNode.getAncestors();
			Set<ChannelNode> descendantChannels = chNode.getDescendants();
			Set<Edge> outEdges = new HashSet<>();
			outEdges.addAll(chNode.getOutEdges());
			for (ChannelNode ancestorCh: ancestorChannels) {
				outEdges.addAll(ancestorCh.getOutEdges());
			}
			for (ChannelNode descendantCh: descendantChannels) {
				outEdges.addAll(descendantCh.getOutEdges());
			}
			for (Edge chToRes: outEdges) {
				Node resNode2 = chToRes.getDestination();
				if (unvisitedNodes.contains(resNode2)) {
					unvisitedNodes.remove(resNode2);
					trackEdgesForwardForPull(resNode2, unvisitedNodes);
				}
			}
		}
	}
}