/*******************************************************************************
* Copyright 2012 Analog Devices, Inc.
*
* 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 com.analog.lyric.dimple.schedulers.dependencyGraph;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import com.analog.lyric.dimple.exceptions.DimpleException;
import com.analog.lyric.dimple.schedulers.dependencyGraph.helpers.LastUpdateGraph;
import com.analog.lyric.dimple.schedulers.schedule.FixedSchedule;
import com.analog.lyric.dimple.schedulers.schedule.ISchedule;
import com.analog.lyric.dimple.schedulers.scheduleEntry.BlockScheduleEntry;
import com.analog.lyric.dimple.schedulers.scheduleEntry.IScheduleEntry;
import com.analog.lyric.dimple.schedulers.scheduleEntry.SubgraphScheduleEntry;
import com.analog.lyric.dimple.solvers.interfaces.ISolverFactorGraph;
/**
* Creates a static dependency graph for a single iteration. The crossiteration dependency graphs
* unroll iterations.
*
* The StaticDependencyGraph consists of StaticDependencyGraphNodes with directed edges
* indicating what shedule entries depend on other schedule entries.
*/
public class StaticDependencyGraph
{
private int _numScheduleEntries;
private ArrayList<StaticDependencyGraphNode> _initialEntries;
private ArrayList<ArrayList<IScheduleEntry>> _phases = new ArrayList<ArrayList<IScheduleEntry>>();
private int _nextNodeId = 0;
/**
* Construct the graph for one iteration
*/
public StaticDependencyGraph(ISolverFactorGraph sfg)
{
this(sfg,1);
}
/**
* Construct the graph.
*/
public StaticDependencyGraph(ISolverFactorGraph sfg,int iters)
{
//Instantiate the data structure that keeps track of the last IScheduleEntry update to touch an edge.
LastUpdateGraph lug = new LastUpdateGraph();
//Initialize the initial entries.
_initialEntries = new ArrayList<StaticDependencyGraphNode>();
//Get the schedule
ISchedule schedule = sfg.getSchedule();
//Do the work of building the dependency graph.
//Allow building dependency graph for multiple iterations
for (int i = 0; i < iters; i++)
{
buildFromSchedule(sfg, schedule,lug);
}
}
/**
* Multithreading can be done in phases of indepdent schedule entries. Each phase
* contains a list of scheduleEntries that can be updated concurrently.
*/
public ArrayList<ArrayList<IScheduleEntry>> getPhases()
{
return _phases;
}
/**
* Produces a GraphViz file for viewing the dependency graph.
* Naming variables and factors will result in a more readable dependency graph.
*/
@SuppressWarnings("resource")
public void createDotFile(String fileName)
{
String str = createDotString();
PrintWriter writer;
try {
writer = new PrintWriter(fileName, "UTF-8");
writer.write(str);
writer.close();
} catch (Exception e) {
throw new DimpleException(e);
}
}
/*
* Produce the GraphViz string to be saved to a dot file.
*/
public String createDotString()
{
//Provide different colors for each phase.
String [] colors = {"red","blue","green","pink","purple","gold","black","cyan"};
StringBuilder sb = new StringBuilder();
sb.append("digraph graphname {\n");
//Keep track of the nodes already added.
HashSet<Integer> foundIds = new HashSet<Integer>();
LinkedList<StaticDependencyGraphNode> nodes = new LinkedList<StaticDependencyGraphNode>();
//Start with the first phase.
for (int i = 0; i < _initialEntries.size(); i++)
{
nodes.push(_initialEntries.get(i));
foundIds.add(_initialEntries.get(i).getId());
}
//until we've output all nodes
while (! nodes.isEmpty())
{
//get the next node.
StaticDependencyGraphNode node = nodes.pop();
//color it based on its phase.
int colorIndex = node.getPhase() % colors.length;
String color = colors[colorIndex];
//print out the node.
sb.append(node.getId() + "[label=\"" + node.getLabel() + "\",color=\"" + color + "\"];\n");
//now print out the edges.
for (int i = 0; i < node.getNumDependents(); i++)
{
StaticDependencyGraphNode nextNode = node.getDependent(i);
sb.append(node.getId() + " -> " + nextNode.getId() + ";\n");
//if we haven't already printed this guy, add it to the list.
if (!foundIds.contains(nextNode.getId()))
{
nodes.add(nextNode);
foundIds.add(nextNode.getId());
}
}
}
sb.append("}\n");
return sb.toString();
}
/*
* How many ScheduleEntry nodes are in this graph.
*/
public int getNumNodes()
{
return _numScheduleEntries;
}
/*
* Returns the StaticDependencyGraphNodes of the first phase.
*/
public ArrayList<StaticDependencyGraphNode> getInitialEntries()
{
return _initialEntries;
}
/*
* Recursive method for building dependency graph from an ISchedule node.
*/
@SuppressWarnings("deprecation")
private void buildFromSchedule(ISolverFactorGraph sfg,
Iterable<? extends IScheduleEntry> schedule,
LastUpdateGraph lug)
{
if (! (schedule instanceof FixedSchedule))
throw new DimpleException("Cannot currently create dependency graph of Dynamic Schedule");
//For each entry in the schedule
for (IScheduleEntry se : schedule)
{
switch (se.type())
{
case SUBGRAPH:
buildFromSchedule(sfg, ((SubgraphScheduleEntry)se).getSubgraphSchedule(sfg), lug);
break;
case SUBSCHEDULE:
buildFromSchedule(sfg, ((com.analog.lyric.dimple.schedulers.scheduleEntry.SubScheduleEntry)se).getSchedule(), lug);
break;
case VARIABLE_BLOCK:
buildFromSchedule(sfg, ((BlockScheduleEntry)se).toNodeEntries(), lug);
break;
case NODE:
case EDGE:
{
//Instantiate a static dependency graph node (builds dependencies)
StaticDependencyGraphNode dgn = new StaticDependencyGraphNode(se,lug,_nextNodeId);
//Increment some counters.
_nextNodeId++;
_numScheduleEntries++;
//Add this entry to the correct phase.
int phase = dgn.getPhase();
while (_phases.size() <= phase)
_phases.add(new ArrayList<IScheduleEntry>());
_phases.get(phase).add(dgn.getScheduleEntry());
//if this is phase 0, add it to the initial entries.
if (phase == 0)
_initialEntries.add(dgn);
break;
}
case CUSTOM:
break;
}
}
}
}