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