/*
* Copyright 1999-2003 Carnegie Mellon University.
* Portions Copyright 2003 Sun Microsystems, Inc.
* Portions Copyright 2003 Mitsubishi Electric Research Laboratories.
* All Rights Reserved. Use is subject to license terms.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
*
*/
package edu.cmu.sphinx.linguist.util;
import edu.cmu.sphinx.linguist.*;
import edu.cmu.sphinx.util.LogMath;
import edu.cmu.sphinx.util.Utilities;
import edu.cmu.sphinx.util.props.*;
import java.io.PrintStream;
import java.util.*;
/** A linguist processor that dumps out the sentence hmm in GDL format. */
public class GDLDumper extends LinguistDumper {
/** The property specifying whether to skip HMMs during dumping. */
@S4Boolean(defaultValue = true)
public static final String PROP_SKIP_HMMS = "skipHMMs";
/** The property to specify whether to use vertical graph layout. */
@S4Boolean(defaultValue = false)
public static final String PROP_VERTICAL_LAYOUT = "verticalLayout";
/** The property to specify whether to dump arc labels. */
@S4Boolean(defaultValue = true)
public static final String PROP_DUMP_ARC_LABELS = "dumpArcLabels";
// -------------------------------
// Configuration data
// --------------------------------
private boolean skipHMMs;
private boolean verticalLayout;
private boolean dumpArcLabels;
private LogMath logMath;
public GDLDumper( String filename, Linguist linguist,
boolean verticalLayout, boolean skipHMMs, boolean dumpArcLabels)
{
super( filename, linguist );
this.verticalLayout = verticalLayout;
this.skipHMMs = skipHMMs;
this.dumpArcLabels = dumpArcLabels;
setDepthFirst(false); // breadth first traversal
logMath = LogMath.getLogMath();
}
public GDLDumper() {
}
/*
* (non-Javadoc)
*
* @see edu.cmu.sphinx.util.props.Configurable#newProperties(edu.cmu.sphinx.util.props.PropertySheet)
*/
@Override
public void newProperties(PropertySheet ps) throws PropertyException {
super.newProperties(ps);
verticalLayout = ps.getBoolean(
PROP_VERTICAL_LAYOUT);
skipHMMs = ps.getBoolean(PROP_SKIP_HMMS);
dumpArcLabels = ps.getBoolean(
PROP_DUMP_ARC_LABELS);
setDepthFirst(false); // breadth first traversal
}
/**
* Retreives the default name for the destination dump. This method is typically overridden by derived classes
*
* @return the default name for the file.
*/
protected String getDefaultName() {
return "linguistDump.gdl";
}
/**
* Called at the start of the dump
*
* @param out the output stream.
*/
@Override
protected void startDump(PrintStream out) {
out.println("graph: {");
out.println(" layout_algorithm: minbackward");
if (verticalLayout) {
out.println(" orientation: top_to_bottom");
out.println(" manhatten_edges: no");
out.println(" splines: yes");
} else {
out.println(" orientation: left_to_right");
out.println(" manhatten_edges: yes");
out.println(" splines: no");
}
}
/**
* Called at the end of the dump
*
* @param out the output stream.
*/
@Override
protected void endDump(PrintStream out) {
out.println("}");
}
/**
* Called to dump out a node in the search space
*
* @param out the output stream.
* @param state the state to dump
* @param level the level of the state
*/
@Override
protected void startDumpNode(PrintStream out, SearchState state, int level) {
if (skipHMMs && (state instanceof HMMSearchState)) {
} else {
String color = getColor(state);
String shape = "box";
out.println(" node: {" + "title: " + qs(getUniqueName(state))
+ " label: " + qs(state.toPrettyString()) + " color: "
+ color + " shape: " + shape + " vertical_order: " + level
+ '}');
}
}
/**
* Gets the color for a particular state
*
* @param state the state
* @return its color
*/
private String getColor(SearchState state) {
String color = "lightred";
if (state.isFinal()) {
color = "magenta";
} else if (state instanceof UnitSearchState) {
color = "green";
} else if (state instanceof WordSearchState) {
color = "lightblue";
} else if (state instanceof HMMSearchState) {
color = "orange";
}
return color;
}
/**
* Called to dump out a node in the search space
*
* @param out the output stream.
* @param state the state to dump
* @param level the level of the state
*/
@Override
protected void endDumpNode(PrintStream out, SearchState state, int level) {
}
/**
* Dumps an arc
*
* @param out the output stream.
* @param from arc leaves this state
* @param arc the arc to dump
* @param level the level of the state
*/
@Override
protected void dumpArc(PrintStream out, SearchState from,
SearchStateArc arc, int level) {
List<SearchStateArc> arcList = new ArrayList<SearchStateArc>();
if (skipHMMs) {
if (from instanceof HMMSearchState) {
return;
} else if (arc.getState() instanceof HMMSearchState) {
findNextNonHMMArc(arc, arcList);
} else {
arcList.add(arc);
}
} else {
arcList.add(arc);
}
for (SearchStateArc nextArc : arcList) {
String label = "";
String color = getArcColor(nextArc);
if (dumpArcLabels) {
double language = logMath.logToLinear(nextArc
.getLanguageProbability());
double insert = logMath.logToLinear(nextArc
.getInsertionProbability());
label = " label: "
+ qs('('
+ formatEdgeLabel(language) + ','
+ formatEdgeLabel(insert) + ')');
}
out.println(" edge: { sourcename: " + qs(getUniqueName(from))
+ " targetname: " + qs(getUniqueName(nextArc.getState()))
+ label + " color: " + color + '}');
}
}
/**
* Given an arc to an HMMSearchState, find a downstream arc to the first non-HMM state
*
* @param arc the arc to start the search at
* @param results the resulting arcs are placed on this list
*/
private void findNextNonHMMArc(SearchStateArc arc, List<SearchStateArc> results) {
Set<SearchStateArc> visited = new HashSet<SearchStateArc>();
List<SearchStateArc> queue = new ArrayList<SearchStateArc>();
queue.add(arc);
while (!queue.isEmpty()) {
SearchStateArc nextArc = queue.remove(0);
if (!visited.contains(nextArc)) {
visited.add(nextArc);
if (!(nextArc.getState() instanceof HMMSearchState)) {
results.add(nextArc);
} else {
queue.addAll(Arrays.asList(nextArc.getState().getSuccessors()));
}
}
}
}
/**
* Formats the given floating point number for edge labels.
*
* @param value the floating point value to format
*/
private String formatEdgeLabel(double value) {
if (value == 1.0) {
return "1";
} else if (value == 0.0) {
return "0";
} else {
int maxStringLength = 5;
String stringValue = String.valueOf(value);
if (stringValue.length() > maxStringLength) {
stringValue = Utilities.doubleToScientificString(value, 3);
}
return stringValue;
}
}
/**
* Returns a color based upon the type of arc
*
* @param arc the arc
* @return the color of the arc based on weather it is a language arc (green), acoustic arc (red), insertion
* arc(blue), flat arc (black) or a combo (purple).
*/
private String getArcColor(SearchStateArc arc) {
String color = null;
if (arc.getLanguageProbability() != 0.0) {
color = "green";
}
if (arc.getInsertionProbability() != 0.0) {
if (color == null) {
color = "blue";
} else {
color = "purple";
}
}
if (color == null) {
color = "black";
}
return color;
}
/**
* Returns a quoted string version of its argument. This method mainly is used to hide the ugliness caused by trying
* to escape a quote character in certain syntax highlighting editors such as vim.
*
* @param s the string to quote.
* @return the quoted string
*/
private String qs(String s) {
return '\"' + s + '\"';
}
/**
* returns a guaranteed unique name for the state
*
* @param state the state of interest
* @return the name
*/
private String getUniqueName(SearchState state) {
return state.getSignature();
}
}