/** * * Copyright 1999-2012 Carnegie Mellon University. * Portions Copyright 2002 Sun Microsystems, Inc. * Portions Copyright 2002 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.fst.operations; import java.util.ArrayList; import java.util.HashSet; import edu.cmu.sphinx.fst.Arc; import edu.cmu.sphinx.fst.Fst; import edu.cmu.sphinx.fst.State; import edu.cmu.sphinx.fst.semiring.Semiring; /** * Connect operation. * * @author John Salatas * */ public class Connect { /** * Calculates the coaccessible states of an fst */ private static void calcCoAccessible(Fst fst, State state, ArrayList<ArrayList<State>> paths, HashSet<State> coaccessible) { // hold the coaccessible added in this loop ArrayList<State> newCoAccessibles = new ArrayList<State>(); for (ArrayList<State> path : paths) { int index = path.lastIndexOf(state); if (index != -1) { if (state.getFinalWeight() != fst.getSemiring().zero() || coaccessible.contains(state)) { for (int j = index; j > -1; j--) { if (!coaccessible.contains(path.get(j))) { newCoAccessibles.add(path.get(j)); coaccessible.add(path.get(j)); } } } } } // run again for the new coaccessibles for (State s : newCoAccessibles) { calcCoAccessible(fst, s, paths, coaccessible); } } /** * Copies a path */ private static void duplicatePath(int lastPathIndex, State fromState, State toState, ArrayList<ArrayList<State>> paths) { ArrayList<State> lastPath = paths.get(lastPathIndex); // copy the last path to a new one, from start to current state int fromIndex = lastPath.indexOf(fromState); int toIndex = lastPath.indexOf(toState); if (toIndex == -1) { toIndex = lastPath.size() - 1; } ArrayList<State> newPath = new ArrayList<State>(lastPath.subList( fromIndex, toIndex)); paths.add(newPath); } /** * The depth first search recursion */ private static State depthFirstSearchNext(Fst fst, State start, ArrayList<ArrayList<State>> paths, ArrayList<Arc>[] exploredArcs, HashSet<State> accessible) { int lastPathIndex = paths.size() - 1; ArrayList<Arc> currentExploredArcs = exploredArcs[start.getId()]; paths.get(lastPathIndex).add(start); if (start.getNumArcs() != 0) { int arcCount = 0; int numArcs = start.getNumArcs(); for (int j = 0; j < numArcs; j++) { Arc arc = start.getArc(j); if ((currentExploredArcs == null) || !currentExploredArcs.contains(arc)) { lastPathIndex = paths.size() - 1; if (arcCount++ > 0) { duplicatePath(lastPathIndex, fst.getStart(), start, paths); lastPathIndex = paths.size() - 1; paths.get(lastPathIndex).add(start); } State next = arc.getNextState(); addExploredArc(start.getId(), arc, exploredArcs); // detect self loops if (next.getId() != start.getId()) { depthFirstSearchNext(fst, next, paths, exploredArcs, accessible); } } } } lastPathIndex = paths.size() - 1; accessible.add(start); return start; } /** * Adds an arc top the explored arcs list */ private static void addExploredArc(int stateId, Arc arc, ArrayList<Arc>[] exploredArcs) { if (exploredArcs[stateId] == null) { exploredArcs[stateId] = new ArrayList<Arc>(); } exploredArcs[stateId].add(arc); } /** * Initialization of a depth first search recursion */ private static void depthFirstSearch(Fst fst, HashSet<State> accessible, ArrayList<ArrayList<State>> paths, ArrayList<Arc>[] exploredArcs, HashSet<State> coaccessible) { State currentState = fst.getStart(); State nextState = currentState; do { if (!accessible.contains(currentState)) { nextState = depthFirstSearchNext(fst, currentState, paths, exploredArcs, accessible); } } while (currentState.getId() != nextState.getId()); int numStates = fst.getNumStates(); for (int i = 0; i < numStates; i++) { State s = fst.getState(i); if (s.getFinalWeight() != fst.getSemiring().zero()) { calcCoAccessible(fst, s, paths, coaccessible); } } } /** * Trims an Fst, removing states and arcs that are not on successful paths. * * @param fst the fst to trim */ public static void apply(Fst fst) { Semiring semiring = fst.getSemiring(); if (semiring == null) { System.out.println("Fst has no semiring."); return; } HashSet<State> accessible = new HashSet<State>(); HashSet<State> coaccessible = new HashSet<State>(); @SuppressWarnings("unchecked") ArrayList<Arc>[] exploredArcs = new ArrayList[fst.getNumStates()]; ArrayList<ArrayList<State>> paths = new ArrayList<ArrayList<State>>(); paths.add(new ArrayList<State>()); depthFirstSearch(fst, accessible, paths, exploredArcs, coaccessible); HashSet<State> toDelete = new HashSet<State>(); for (int i = 0; i < fst.getNumStates(); i++) { State s = fst.getState(i); if (!(accessible.contains(s) || coaccessible.contains(s))) { toDelete.add(s); } } fst.deleteStates(toDelete); } }