/** * Copyright 2007-2008 University Of Southern California * * 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 edu.isi.pegasus.planner.partitioner; import edu.isi.pegasus.common.logging.LogManagerFactory; import edu.isi.pegasus.planner.common.PegasusProperties; import edu.isi.pegasus.common.logging.LogManager; import edu.isi.pegasus.planner.partitioner.graph.GraphNode; import edu.isi.pegasus.planner.partitioner.graph.Bag; import edu.isi.pegasus.planner.partitioner.graph.LabelBag; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.LinkedList; import java.util.Iterator; import java.util.Set; import java.util.HashSet; /** * This partitioner partitions the DAX into smaller partitions as specified by * the labels associated with the jobs. If no label is specified, then the * partitioner puts the job into a unique partition corresponding to the job * ID. * * @author Karan Vahi * @version $Revision$ * * */ public class Label extends Partitioner { /** * The default label that is associated with the job in case of no label * being specified. */ // public static final String DEFAULT_LABEL = "default"; /** * A short description about the partitioner. */ public static final String DESCRIPTION = "Label Based Partitioning"; /** * A map indexed by the label. Each value is a partition object * consisting of jobs with that label. */ private Map mPartitionMap; /** * The first in first out queue, that manages the set of gray vertices in a * breadth first search. */ private LinkedList mQueue; /** * The handle to the Logging object. */ private LogManager mLogger; /** * The overloaded constructor. * * @param root the dummy root node of the graph. * @param graph the map containing all the nodes of the graph keyed by * the logical id of the nodes. * @param properties the properties passed to the planner. */ public Label(GraphNode root, Map graph, PegasusProperties properties) { super(root, graph, properties); mPartitionMap = new HashMap(10); mQueue = new LinkedList(); mLogger = LogManagerFactory.loadSingletonInstance( properties ); } /** * Partitions the graph passed in the constructor, on the basis of the labels * associated with the nodes in the graph. All the nodes, with the same label * are deemed to be in the same partition. * * @param c the callback for the partitioner. */ public void determinePartitions( Callback c ){ int currentDepth = 0; GraphNode node; GraphNode parent; GraphNode child; int depth = 0; List levelList = new java.util.LinkedList(); String currentLabel = null; int i = 0,partitionNum = 0; mLogger.log( "Starting Graph Traversal", LogManager.INFO_MESSAGE_LEVEL ); //set the depth of the dummy root as 0 mRoot.setDepth( currentDepth ); mQueue.addLast( mRoot ); while( !mQueue.isEmpty() ){ node = (GraphNode)mQueue.getFirst(); depth = node.getDepth(); currentLabel = getLabel( node ); if(currentDepth < depth){ //a new level starts currentDepth++; levelList.clear(); } //get the partition for the label Partition p = null; if( mPartitionMap.containsKey( currentLabel ) ){ p = (Partition)mPartitionMap.get( currentLabel ); } else { p = new Partition(); if( currentDepth > 0 ){ partitionNum++; p.setIndex( partitionNum ); p.setID(getPartitionID( partitionNum )); mPartitionMap.put( currentLabel, p ); } } if( p.lastAddedNode()!= null && depth > p.lastAddedNode().getDepth() + 1 ){ throw new RuntimeException( "Invalid labelled graph" ); /* //partition with current label has been fully //constructed. write out the existing partition //create a new partition Partition newp = new Partition(); newp.addNode(node); mPartitionMap.put(currentLabel,newp); */ } else if(currentDepth > 0){ //add to the existing partition for the current label p.addNode(node); //also associate the partition id with the node node.getBag().add( LabelBag.PARTITION_KEY,p.getID() ); } mLogger.log("Adding to level " + currentDepth + " " + node.getID(),LogManager.DEBUG_MESSAGE_LEVEL); levelList.add( node ); node.setColor( GraphNode.BLACK_COLOR ); for( Iterator it = node.getChildren().iterator(); it.hasNext(); ){ child = (GraphNode)it.next(); if(!child.isColor( GraphNode.GRAY_COLOR ) && child.parentsColored( GraphNode.BLACK_COLOR ) ){ mLogger.log( "Adding to queue " + child.getID(), LogManager.DEBUG_MESSAGE_LEVEL ); child.setDepth( depth + 1 ); child.setColor( GraphNode.GRAY_COLOR ); mQueue.addLast( child ); } } node = (GraphNode)mQueue.removeFirst(); mLogger.log( "Removed " + node.getID(), LogManager.DEBUG_MESSAGE_LEVEL ); } mLogger.log( "Starting Graph Traversal - DONE", LogManager.INFO_MESSAGE_LEVEL ); for( Iterator it = mPartitionMap.entrySet().iterator(); it.hasNext(); ){ Map.Entry entry = (Map.Entry)it.next(); Partition p = (Partition)entry.getValue(); p.constructPartition(); mLogger.log( "Partition is " + p.getNodeIDs() + " corresponding to label " + entry.getKey(), LogManager.DEBUG_MESSAGE_LEVEL ); //PM-745 set whether a single sized partition was associated with a label //or not. any partition of size > 1 has to have a label assoicated. boolean hasAssociatedLabel = true; if( p.getSize() == 1 ){ //for single sized partitions look into the last added node //and check whether the label is equal the node id GraphNode n = p.lastAddedNode(); if( this.getLabel( n ).equals( n.getID() ) ){ //user did not associated a specific label key hasAssociatedLabel = false ; } } p.doesHaveAssociatedLabel( hasAssociatedLabel ); //call the callback c.cbPartition( p ); } mLogger.log( "Determining relations between partitions", LogManager.INFO_MESSAGE_LEVEL ); //construct the relations for( Iterator it = mPartitionMap.entrySet().iterator(); it.hasNext(); ){ Map.Entry entry = (Map.Entry) it.next(); Partition p = (Partition) entry.getValue(); List roots = p.getRootNodes(); Set parentPartitions = new HashSet( roots.size() ); //get the Root nodes for each partition and //for each root, determine the partitions of it's parents for( Iterator rootIt = roots.iterator(); rootIt.hasNext(); ){ node = (GraphNode)rootIt.next(); for( Iterator parentsIt = node.getParents().iterator(); parentsIt.hasNext(); ){ parent = (GraphNode)parentsIt.next(); //the parents partition id is parent for the //partition containing the root parentPartitions.add( parent.getBag().get( LabelBag.PARTITION_KEY ) ); } } //write out all the parents of the partition if(!parentPartitions.isEmpty()){ c.cbParents( p.getID(), new ArrayList( parentPartitions ) ); } } mLogger.log( "Determining relations between partitions - DONE", LogManager.INFO_MESSAGE_LEVEL ); //done with the partitioning c.cbDone(); } /** * Returns a textual description of the transfer implementation. * * @return a short textual description */ public String description(){ return this.DESCRIPTION; } /** * Returns the label for the node. If no label is associated with the node, * then the ID of the node is assumed as the label. * * @param node the node for which the label is required. * * @return the label associated with the job, else the id of the node. */ private String getLabel(GraphNode node){ Bag b = (LabelBag)node.getBag(); Object obj = b.get( LabelBag.LABEL_KEY ); return (obj == null )? node.getID() /*this.DEFAULT_LABEL*/ : (String)obj; } /** * Constructs the id for the partition. * * @param id the integer id. * * @return the ID of the partition. */ private String getPartitionID( int id ){ StringBuffer sb = new StringBuffer(5); sb.append( "ID" ).append( id ); return sb.toString(); } }