package edu.stanford.nlp.fsm;
import edu.stanford.nlp.stats.ClassicCounter;
import java.util.*;
/**
* QuasiDeterminizer.
* <p/>
* Class for performing quasi-determinization on TransducerGraphs.
*
* @author Teg Grenager
* @version 11/02/03
*/
public class QuasiDeterminizer implements TransducerGraph.GraphProcessor {
public TransducerGraph processGraph(TransducerGraph graph) {
// compute lambda function
ClassicCounter lambda = computeLambda(graph); // not destructive
// do the pushing
TransducerGraph result = pushLambdas(graph, lambda); // creates a new one
return result;
}
/**
* Takes time linear in number of arcs.
*/
public static ClassicCounter computeLambda(TransducerGraph graph) {
LinkedList queue = new LinkedList();
ClassicCounter lambda = new ClassicCounter();
ClassicCounter length = new ClassicCounter();
Map first = new HashMap();
Set nodes = graph.getNodes();
for (Object node : nodes) {
lambda.setCount(node, 0);
length.setCount(node, Double.POSITIVE_INFINITY);
}
Set endNodes = graph.getEndNodes();
for (Object o : endNodes) {
lambda.setCount(o, 0);
length.setCount(o, 0);
queue.addLast(o);
}
// Breadth first search
// get the first node from the queue
Object node = null;
try {
node = queue.removeFirst();
} catch (NoSuchElementException e) {
}
while (node != null) {
double oldLen = length.getCount(node);
Set arcs = graph.getArcsByTarget(node);
if (arcs != null) {
for (Object arc1 : arcs) {
TransducerGraph.Arc arc = (TransducerGraph.Arc) arc1;
Object newNode = arc.getSourceNode();
Comparable a = (Comparable) arc.getInput();
double k = ((Double) arc.getOutput()).doubleValue();
double newLen = length.getCount(newNode);
if (newLen == Double.POSITIVE_INFINITY) {
// we are discovering this
queue.addLast(newNode);
}
Comparable f = (Comparable) first.get(newNode);
if (newLen == Double.POSITIVE_INFINITY || (newLen == oldLen + 1 && a.compareTo(f) < 0)) { // f can't be null, since we have a newLen
// we do this to this to newNode when we have new info, possibly many times
first.put(newNode, a); // ejecting old one if necessary
length.setCount(newNode, oldLen + 1); // this may already be the case
lambda.setCount(newNode, k + lambda.getCount(node));
}
}
}
// get a new node from the queue
node = null;
try {
node = queue.removeFirst();
} catch (NoSuchElementException e) {
}
}
return lambda;
}
/**
* Takes time linear in number of arcs.
*/
public TransducerGraph pushLambdas(TransducerGraph graph, ClassicCounter lambda) {
TransducerGraph result = null;
try {
result = graph.clone(); // arcs have been copied too so we don't mess up graph
} catch (CloneNotSupportedException cnse) {
throw new RuntimeException(cnse);
}
Set<TransducerGraph.Arc> arcs = result.getArcs();
for (TransducerGraph.Arc arc : arcs) {
double sourceLambda = lambda.getCount(arc.getSourceNode());
double targetLambda = lambda.getCount(arc.getTargetNode());
double oldOutput = ((Double) arc.getOutput()).doubleValue();
double newOutput = oldOutput + targetLambda - sourceLambda;
arc.setOutput(new Double(newOutput));
}
// do initialOutput
double startLambda = lambda.getCount(result.getStartNode());
if (startLambda != 0.0) {
// add it back to the outbound arcs from start (instead of adding it to the initialOutput)
Set<TransducerGraph.Arc> startArcs = result.getArcsBySource(result.getStartNode());
for (TransducerGraph.Arc arc : startArcs) {
double oldOutput = ((Double) arc.getOutput()).doubleValue();
double newOutput = oldOutput + startLambda;
arc.setOutput(new Double(newOutput));
}
}
// do finalOutput
for (Object o : result.getEndNodes()) {
double endLambda = lambda.getCount(o);
if (endLambda != 0.0) {
// subtract it from the inbound arcs to end (instead of subtracting it from the finalOutput)
Set<TransducerGraph.Arc> endArcs = result.getArcsByTarget(o);
for (TransducerGraph.Arc arc : endArcs) {
double oldOutput = ((Double) arc.getOutput()).doubleValue();
double newOutput = oldOutput - endLambda;
arc.setOutput(new Double(newOutput));
}
}
}
return result;
}
public static void main(String[] args) {
TransducerGraph.GraphProcessor qd = new QuasiDeterminizer();
List pathList = new ArrayList();
TransducerGraph graph = TransducerGraph.createRandomGraph(1000, 10, 1.0, 10, pathList);
StringBuilder b = new StringBuilder();
graph.depthFirstSearch(true, b);
System.out.println(b.toString());
System.out.println("Done creating random graph");
// TransducerGraph.printPathOutputs(pathList, graph, false);
//System.out.println("Depth first search from start node");
//TransducerGraph.depthFirstSearch(graph, TransducerGraph.END_NODE, new HashSet(), 0, false);
TransducerGraph newGraph = qd.processGraph(graph);
System.out.println("Done quasi-determinizing");
//TransducerGraph.printPathOutputs(pathList, newGraph, false);
//System.out.println("Depth first search from start node");
//TransducerGraph.depthFirstSearch(newGraph, TransducerGraph.END_NODE, new HashSet(), 0, false);
TransducerGraph.testGraphPaths(graph, newGraph, 1000);
}
}