/** * 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.LogManager; import edu.isi.pegasus.planner.common.PegasusProperties; import edu.isi.pegasus.planner.partitioner.graph.GraphNode; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ArrayList; import java.util.Map; /** * This does a modified breadth first search of the graph to identify the levels. * A node is put in a level only if all the parents of that node are already * assigned a level. * * @author Karan Vahi * @version $Revision$ */ public class BFS extends Partitioner{ /** * A short description about the partitioner. */ public static final String DESCRIPTION = "Level Based Partitioning"; /** * The first in first out queue, that manages the set of gray vertices in a * breadth first search. */ private LinkedList mQueue; /** * The current depth of the nodes that are being traversed in the BFS. */ private int mCurrentDepth; /** * 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 BFS(GraphNode root, Map graph, PegasusProperties properties) { super(root,graph,properties); mQueue = new LinkedList(); mCurrentDepth = -1; } /** * Does a constrained breadth first search to identify the partitions, and * calls out to write out the partition graph. * * @param c the callback for the partitioner. */ public void determinePartitions( Callback c ){ mCurrentDepth = 0; GraphNode node; GraphNode child; int depth = 0; List levelList = new java.util.LinkedList(); int i = 0; //they contain those nodes whose parents have not been traversed as yet //but the BFS did it. List orphans = new java.util.LinkedList(); //set the depth of the dummy root as 0 mRoot.setDepth( mCurrentDepth ); mQueue.addLast( mRoot ); while( !mQueue.isEmpty() ){ node = (GraphNode)mQueue.getFirst(); depth = node.getDepth(); if( mCurrentDepth < depth ){ if( mCurrentDepth > 0 ){ //we are done with one level! constructPartitions( c, levelList, mCurrentDepth ); } //a new level starts mCurrentDepth++; levelList.clear(); } mLogger.log( "Adding to level " + mCurrentDepth + " " + node.getID(), LogManager.DEBUG_MESSAGE_LEVEL); levelList.add( node ); //look at the orphans first to see if any //of the dependency has changed or not. /*it = orphans.iterator(); while(it.hasNext()){ child = (GraphNode)it.next(); if(child.parentsBlack()){ child.setDepth(depth + 1); System.out.println("Set depth of " + child.getID() + " to " + child.getDepth()); child.traversed(); mQueue.addLast(child); } //remove the child from the orphan it.remove(); }*/ 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 ); } /*else if(!child.isTraversed() && !child.parentsBlack()){ //we have to do the bumping effect System.out.println("Bumping child " + child); orphans.add(child); }*/ } node = (GraphNode)mQueue.removeFirst(); mLogger.log( "Removed " + node.getID(), LogManager.DEBUG_MESSAGE_LEVEL); } //handle the last level of the BFS constructPartitions( c, levelList, mCurrentDepth ); //all the partitions are dependant sequentially for( i = mCurrentDepth; i > 1; i-- ){ constructLevelRelations( c, i - 1, i ); } done( c ); } /** * Returns a textual description of the transfer implementation. * * @return a short textual description */ public String description(){ return this.DESCRIPTION; } /** * Given a list of jobs, constructs (one or more) partitions out of it. * Calls out to the partitioner callback, for each of the partitions * constructed. * * @param c the parititoner callback * @param nodes the list of <code>GraphNode</code> objects on a particular level. * @param level the level as determined from the root of the workflow. */ protected void constructPartitions( Callback c, List nodes, int level ){ //we want to ignore the dummy node partition String id = getPartitionID( mCurrentDepth ); Partition p = new Partition( nodes, id ); p.setIndex( mCurrentDepth ); p.constructPartition(); mLogger.log( "Partition " + p.getID() + " is :" + p.getNodeIDs(), LogManager.DEBUG_MESSAGE_LEVEL ); c.cbPartition( p ); } /** * Calls out to the callback with appropriate relations between the partitions * constructed for the levels. * * @param c the parititoner callback * @param parent the parent level * @param child the child level. */ protected void constructLevelRelations( Callback c, int parent, int child ){ String childID = getPartitionID( child ); String parentID = getPartitionID( parent ); List parents = new ArrayList(1); parents.add( parentID ); c.cbParents( childID, parents ); } /** * Indicates that we are done with the partitioning. * Calls out to the appropriate callback function */ protected void done( Callback c ){ //done with the partitioning c.cbDone(); } /** * Constructs the id for the partition. * * @param level the depth from the root of the graph. * * @return the ID for the Partition. */ private String getPartitionID(int level){ StringBuffer sb = new StringBuffer(5); sb.append("ID").append(level); return sb.toString(); } }