1 /*************************************************************************/
2 /*                                                                       */
3 /*                Centre for Speech Technology Research                  */
4 /*                     University of Edinburgh, UK                       */
5 /*                         Copyright (c) 1997                            */
6 /*                        All Rights Reserved.                           */
7 /*                                                                       */
8 /*  Permission is hereby granted, free of charge, to use and distribute  */
9 /*  this software and its documentation without restriction, including   */
10 /*  without limitation the rights to use, copy, modify, merge, publish,  */
11 /*  distribute, sublicense, and/or sell copies of this work, and to      */
12 /*  permit persons to whom this work is furnished to do so, subject to   */
13 /*  the following conditions:                                            */
14 /*   1. The code must retain the above copyright notice, this list of    */
15 /*      conditions and the following disclaimer.                         */
16 /*   2. Any modifications must be clearly marked as such.                */
17 /*   3. Original authors' names are not deleted.                         */
18 /*   4. The authors' names are not used to endorse or promote products   */
19 /*      derived from this software without specific prior written        */
20 /*      permission.                                                      */
21 /*                                                                       */
22 /*  THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK        */
23 /*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
24 /*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
25 /*  SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE     */
26 /*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
27 /*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
28 /*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
29 /*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
30 /*  THIS SOFTWARE.                                                       */
31 /*                                                                       */
32 /*************************************************************************/
33 /*                     Author :  Alan W Black                            */
34 /*                     Date   :  December 1997                           */
35 /*-----------------------------------------------------------------------*/
36 /*                                                                       */
37 /* Transduction using a WFST                                             */
38 /*                                                                       */
39 /*=======================================================================*/
40 #include <iostream>
41 #include "EST_WFST.h"
42 
43 /** An internal class in transduction of WFSTs holding intermediate
44     state information.
45 */
46 class wfst_tstate {
47   public:
48     int state;
49     EST_IList outs;
50     float score;
51 };
52 
operator <<(ostream & s,const wfst_tstate & state)53 ostream &operator << (ostream &s, const wfst_tstate &state)
54 {
55   (void)state;
56   return s << "<<wfst_tstate>>";
57 }
58 
59 Declare_TList(wfst_tstate)
60 
61 #if defined(INSTANTIATE_TEMPLATES)
62 #include "../base_class/EST_TList.cc"
63 
64 Instantiate_TList(wfst_tstate)
65 
66 #endif
67 
68 typedef EST_TList<wfst_tstate> wfst_tstate_list;
69 
70 static void add_transduce_mstate(const EST_WFST &wfst,
71 				 const wfst_tstate &cs,
72 				 wfst_translist &tranlist,
73 				 wfst_tstate_list &ns);
74 
transduce(const EST_WFST & wfst,const EST_StrList & in,EST_StrList & out)75 int transduce(const EST_WFST &wfst,const EST_StrList &in,EST_StrList &out)
76 {
77     // Map names to internal ints before transduction
78     EST_Litem *p;
79     EST_IList in_i,out_i;
80     int r;
81 
82     for (p=in.head(); p != 0; p=p->next())
83 	in_i.append(wfst.in_symbol(in(p)));
84 
85     r = transduce(wfst,in_i,out_i);
86 
87     for (p=out_i.head(); p != 0; p=p->next())
88 	out.append(wfst.out_symbol(out_i(p)));
89 
90     return r;
91 }
92 
transduce(const EST_WFST & wfst,const EST_IList & in,EST_IList & out)93 int transduce(const EST_WFST &wfst,const EST_IList &in,EST_IList &out)
94 {
95     // Transduce input stream to an output stream
96     EST_Litem *i,*cs;
97     int r=FALSE;
98     wfst_tstate_list *current_ms = new wfst_tstate_list;
99     wfst_tstate start_state;
100     wfst_translist ss_eps_trans;
101 
102     start_state.state = wfst.start_state();
103     start_state.score = 0;
104     current_ms->append(start_state);
105     // Add any epsilon accessible states
106     wfst.transduce(wfst.start_state(),wfst.in_epsilon(),ss_eps_trans);
107     add_transduce_mstate(wfst,start_state,ss_eps_trans,*current_ms);
108 
109     for (i=in.head(); i != 0; i=i->next())
110     {
111 	wfst_tstate_list *ns = new wfst_tstate_list;
112 
113 	for (cs=current_ms->head(); cs != 0; cs=cs->next())
114 	{   // For each state in current update list of new states
115 	    wfst_translist translist;
116 	    wfst.transduce((*current_ms)(cs).state,in(i),translist);
117 	    add_transduce_mstate(wfst,(*current_ms)(cs),translist,*ns);
118 	}
119 	// Using pointers to avoid having to copy the state list
120 	delete current_ms;
121 	current_ms = ns;
122 
123 	if (current_ms->length() == 0)
124 	    break;  // give up, no transition possible
125     }
126     // current_ms will contain the list of possible transitions
127     if (current_ms->length() > 1)
128 	cerr << "WFST: found " << current_ms->length() << " transductions" <<
129 	    endl;
130     // should find "best" but we'll find longest at present
131     // Choose the longest (should be based on score)
132     for (cs = current_ms->head(); cs != 0; cs=cs->next())
133     {
134 	if ((wfst.final((*current_ms)(cs).state)) &&
135 	    ((*current_ms)(cs).outs.length() > out.length()))
136 	{
137 	    r = TRUE;
138 	    out = (*current_ms)(cs).outs;
139 	}
140     }
141     delete current_ms;
142     return r;
143 }
144 
add_transduce_mstate(const EST_WFST & wfst,const wfst_tstate & cs,wfst_translist & translist,wfst_tstate_list & ns)145 static void add_transduce_mstate(const EST_WFST &wfst,
146 				 const wfst_tstate &cs,
147 				 wfst_translist &translist,
148 				 wfst_tstate_list &ns)
149 {
150     // For each possible transduction of in from cs.state in WFST
151     // add it to ns.
152     // This should really only add states if they are not already there
153     // but you really need stae plus recognized path to get all
154     // transductions so a tree structure would be better.
155     EST_Litem *t;
156 
157     // Add new states to ns if not already there
158     for (t=translist.head(); t != 0; t=t->next())
159     {
160 	// Declare a new one and put it on the end of the list
161 	// before we fill its values, this saves a copy
162 	wfst_tstate tts;
163 	ns.append(tts);
164 	wfst_tstate &ts = ns.last();
165 
166 	ts.state = translist(t)->state();
167 	// the combination is probably WFST dependant
168 	ts.score = translist(t)->weight()+cs.score;
169 	// copying the outs up to now (pity)
170 	ts.outs = cs.outs;
171 	ts.outs.append(translist(t)->out_symbol());
172 
173 	// Also any potential epsilon transitions for this new state
174 	wfst_translist etranslist;
175 	wfst.transduce(ts.state,wfst.in_epsilon(),etranslist);
176 	add_transduce_mstate(wfst,ts,etranslist,ns);
177     }
178 }
179 
recognize(const EST_WFST & wfst,const EST_StrList & in,int quiet)180 int recognize(const EST_WFST &wfst,const EST_StrList &in,int quiet)
181 {
182     // Map names to internal ints before recognition
183     EST_Litem *p;
184     EST_IList in_i,out_i;
185     int i,o;
186     int r;
187 
188     for (p=in.head(); p != 0; p=p->next())
189     {
190 	if (in(p).contains("/"))
191 	{
192 	    i = wfst.in_symbol(in(p).before("/"));
193 	    o = wfst.out_symbol(in(p).after("/"));
194 	}
195 	else
196 	{
197 	    i = wfst.in_symbol(in(p));
198 	    o = wfst.out_symbol(in(p));
199 	}
200 	in_i.append(i);
201 	out_i.append(o);
202     }
203 
204     r = recognize(wfst,in_i,out_i,quiet);
205 
206     return r;
207 }
208 
recognize(const EST_WFST & wfst,const EST_IList & in,const EST_IList & out,int quiet)209 int recognize(const EST_WFST &wfst,const EST_IList &in,
210 	      const EST_IList &out, int quiet)
211 {
212     int state = wfst.start_state();
213     EST_Litem *p,*q;
214     int nstate;
215 
216     for (p=in.head(),q=out.head();
217 	 ((p != 0) && (q != 0));
218 	 p=p->next(),q=q->next())
219     {
220 	nstate = wfst.transition(state,in(p),out(q));
221 	if (!quiet)
222 	    printf("state %d %s/%s -> %d\n",state,
223 		   (const char *)wfst.in_symbol(in(p)),
224 		   (const char *)wfst.out_symbol(out(q)),
225 		   nstate);
226 	state = nstate;
227 	if (state == WFST_ERROR_STATE)
228 	    return FALSE;
229     }
230 
231     if (p != q)
232     {
233 	cerr << "wfst recognize: in/out tapes of different lengths"
234 	    << endl;
235 	return FALSE;
236     }
237 
238     if (wfst.final(state))
239 	return TRUE;
240     else
241 	return FALSE;
242 }
243 
recognize_for_perplexity(const EST_WFST & wfst,const EST_StrList & in,int quiet,float & count,float & sumlogp)244 int recognize_for_perplexity(const EST_WFST &wfst,
245 			     const EST_StrList &in,
246 			     int quiet,
247 			     float &count,
248 			     float &sumlogp)
249 {
250     // Map names to internal ints before recognition
251     EST_Litem *p;
252     EST_IList in_i,out_i;
253     int i,o;
254     int r;
255 
256     for (p=in.head(); p != 0; p=p->next())
257     {
258 	if (in(p).contains("/"))
259 	{
260 	    i = wfst.in_symbol(in(p).before("/"));
261 	    o = wfst.out_symbol(in(p).after("/"));
262 	}
263 	else
264 	{
265 	    i = wfst.in_symbol(in(p));
266 	    o = wfst.out_symbol(in(p));
267 	}
268 	in_i.append(i);
269 	out_i.append(o);
270     }
271 
272     r = recognize_for_perplexity(wfst,in_i,out_i,quiet,count,sumlogp);
273 
274     return r;
275 }
276 
recognize_for_perplexity(const EST_WFST & wfst,const EST_IList & in,const EST_IList & out,int quiet,float & count,float & sumlogp)277 int recognize_for_perplexity(const EST_WFST &wfst,
278 			     const EST_IList &in,
279 			     const EST_IList &out,
280 			     int quiet,
281 			     float &count,
282 			     float &sumlogp)
283 {
284     int state = wfst.start_state();
285     EST_Litem *p,*q;
286     int nstate;
287     float prob;
288     count = 0;
289     sumlogp = 0;
290 
291     for (p=in.head(),q=out.head();
292 	 ((p != 0) && (q != 0));
293 	 p=p->next(),q=q->next())
294     {
295 	nstate = wfst.transition(state,in(p),out(q),prob);
296 	count++;
297 	if (prob > 0)
298 	    sumlogp += log(prob);
299 	else
300 	    sumlogp += -100;  // bad hack
301 	if (!quiet)
302 	    printf("state %d %s/%s -> %d\n",state,
303 		   (const char *)wfst.in_symbol(in(p)),
304 		   (const char *)wfst.out_symbol(out(q)),
305 		   nstate);
306 	state = nstate;
307 	if (state == WFST_ERROR_STATE)
308 	    return FALSE;
309     }
310 
311     if (p != q)
312     {
313 	cerr << "wfst recognize: in/out tapes of different lengths"
314 	    << endl;
315 	return FALSE;
316     }
317 
318     if (wfst.final(state))
319 	return TRUE;
320     else
321 	return FALSE;
322 }
323 
324