1 /*************************************************************************/
2 /*                                                                       */
3 /*                Centre for Speech Technology Research                  */
4 /*                     University of Edinburgh, UK                       */
5 /*                       Copyright (c) 1996,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 /*                 Authors:  Alan W Black                                */
34 /*                 Date   :  July 1996                                   */
35 /*-----------------------------------------------------------------------*/
36 /*  A viterbi decoder                                                    */
37 /*                                                                       */
38 /*  User provides the candidates, target and combine score function      */
39 /*  and it searches for the best path through the candidates             */
40 /*                                                                       */
41 /*=======================================================================*/
42 #include <cstdio>
43 #include "EST_viterbi.h"
44 
45 static void init_paths_array(EST_VTPoint *n,int num_states);
46 static void debug_output_1(EST_VTPoint *p,EST_VTCandidate *c,
47 			   EST_VTPath *np, int i);
48 
~EST_VTPoint()49 EST_VTPoint::~EST_VTPoint()
50 {
51     int i;
52 
53     if (paths != 0) delete paths;
54     if (num_states != 0)
55     {
56 	for (i=0; i<num_states; i++)
57 	    if (st_paths[i] != 0)
58 		delete st_paths[i];
59 	delete [] st_paths;
60     }
61     if (cands != 0) delete cands;
62     if (next != 0) delete next;
63 }
64 
EST_Viterbi_Decoder(uclist_f_t a,unpath_f_t b)65 EST_Viterbi_Decoder::EST_Viterbi_Decoder(uclist_f_t a, unpath_f_t b)
66   : vit_a_big_number(1.0e10)
67 {
68     beam_width = 0;
69     cand_width = 0;
70     user_clist = a;
71     user_npath = b;
72     num_states = 0;
73 
74     do_pruning = FALSE;
75     overall_path_pruning_envelope_width = -1;
76     candidate_pruning_envelope_width = -1;
77 
78     debug = FALSE;
79     trace = FALSE;
80     big_is_good = TRUE;  // for probabilities it is
81 }
82 
EST_Viterbi_Decoder(uclist_f_t a,unpath_f_t b,int s)83 EST_Viterbi_Decoder::EST_Viterbi_Decoder(uclist_f_t a, unpath_f_t b, int s)
84   : vit_a_big_number(1.0e10)
85 {
86     beam_width = 0;
87     cand_width = 0;
88     user_clist = a;
89     user_npath = b;
90 
91     do_pruning = FALSE;
92     overall_path_pruning_envelope_width = -1;
93     candidate_pruning_envelope_width = -1;
94 
95     // if s == -1 number of states is determined by number of cands
96     // this is specific to a particular search but very efficient
97     num_states = s;  // can do the lattice search more directly
98     debug = FALSE;
99     trace = FALSE;
100     big_is_good = TRUE;  // for probabilities it is
101 }
102 
~EST_Viterbi_Decoder()103 EST_Viterbi_Decoder::~EST_Viterbi_Decoder()
104 {
105     delete timeline;
106 }
107 
initialise(EST_Relation * p)108 void EST_Viterbi_Decoder::initialise(EST_Relation *p)
109 {
110     // Creates a time line with each point pointing at each item in p
111     EST_Item *i;
112     EST_VTPoint *t = 0,*n;
113 
114     for (i=p->head(); i != 0; i=i->next())
115     {
116 	n = new EST_VTPoint;
117 	n->s = i;
118 	if (num_states > 0)
119 	    init_paths_array(n,num_states);
120 	if (t == 0)
121 	    timeline = n;
122 	else
123 	    t->next = n;
124 	t=n;
125     }
126     // Extra one at the end for final paths
127     n = new EST_VTPoint;
128     if (num_states > 0)
129 	init_paths_array(n,num_states);
130 
131     // Need init path on first point so a search can start
132     if (num_states == 0)   // general search case
133 	timeline->paths = new EST_VTPath;
134     if (num_states == -1)
135 	init_paths_array(timeline,1);  // a start point
136 
137     if (t == 0)
138 	timeline = n;   // its an empty stream
139     else
140 	t->next = n;
141 }
142 
init_paths_array(EST_VTPoint * n,int num_states)143 static void init_paths_array(EST_VTPoint *n,int num_states)
144 {
145     // Create the states array and initialize it
146     int j;
147 
148     n->num_states = num_states;
149     n->st_paths = new EST_VTPath*[num_states];
150     for (j=0;j<num_states;j++)
151 	n->st_paths[j] = 0;
152 }
153 
betterthan(const float a,const float b) const154 const int EST_Viterbi_Decoder::betterthan(const float a,const float b) const
155 {
156     // Some thing big is better, others want it to be as small as possible
157     // this just tells you if a is better than b by checking the variable
158     // in the decoder itself.
159 
160     // For probabilities big_is_good == TRUE (or log probabilities)
161 
162     if (big_is_good)
163 	return (a > b);
164     else
165 	return (a < b);
166 }
167 
init_dynamic_states(EST_VTPoint * p,EST_VTCandidate * cands)168 static int init_dynamic_states(EST_VTPoint *p, EST_VTCandidate *cands)
169 {
170     // In a special (hmm maybe not so special), the number of "states"
171     // is the number of candidates
172     EST_VTCandidate *c;
173     int i;
174 
175     for (i=0, c=cands; c != 0; c=c->next,i++)
176 	c->pos = i;
177     init_paths_array(p,i);
178 
179     return i;
180 }
181 
set_pruning_parameters(float beam,float ob_beam)182 void EST_Viterbi_Decoder::set_pruning_parameters(float beam, float
183 						 ob_beam)
184 {
185 
186     do_pruning = TRUE;
187     overall_path_pruning_envelope_width = beam;
188     candidate_pruning_envelope_width = ob_beam;
189 
190 }
191 
turn_on_debug()192 void EST_Viterbi_Decoder::turn_on_debug()
193 {
194     debug = TRUE;
195 }
196 
turn_on_trace()197 void EST_Viterbi_Decoder::turn_on_trace()
198 {
199     trace = TRUE;
200 }
201 
202 
search(void)203 void EST_Viterbi_Decoder::search(void)
204 {
205     // Searches for the best path
206     EST_VTPoint *p;
207     EST_VTPath *t,*np;
208     EST_VTCandidate *c;
209     int i=0;
210 
211     double best_score=0.0,score_cutoff=0.0;
212     double best_candidate_score=0.0,candidate_cutoff=0;
213     int dcount,pcount;
214     int cand_count=0, cands_considered=0;
215 
216     for (p=timeline; p->next != 0; p=p->next)
217     {   // For each point in time
218 	// Find the candidates
219 	p->cands  = (*user_clist)(p->s,f);  // P(S|B)
220 	if (do_pruning)
221 	    prune_initialize(p,best_score,best_candidate_score,
222 			     score_cutoff,candidate_cutoff,
223 			     cand_count);
224 	if (num_states != 0)  // true viterbi -- optimized for states
225 	{
226 	    if (num_states == -1)  // special case, dynamic state size
227 		init_dynamic_states(p->next,p->cands);
228 
229 	    cands_considered=0; // moved by simonk
230 	    for (i=0; i<p->num_states; i++)
231 	    {   // for each path up to here
232 		//cands_considered=0;
233 		if (((p == timeline) && i==0) || (p->st_paths[i] != 0))
234 		    for (c=p->cands; c != 0; c=c->next)
235 		    {
236 			// for each new candidate
237 			// candidate pruning (a.k.a. observation pruning)
238 			if(!do_pruning ||
239 			   betterthan(c->score,candidate_cutoff))
240 			{
241 			    cands_considered++;
242 			    // Find path extension costs
243 			    np = (*user_npath)(p->st_paths[i],c,f);
244 			    if (debug) debug_output_1(p,c,np,i);
245 			    if (do_pruning && betterthan(np->score,best_score))
246 			    {
247 				best_score = np->score;
248 				if(big_is_good)
249 				    score_cutoff = best_score
250 					- overall_path_pruning_envelope_width;
251 				else
252 				    score_cutoff = best_score
253 					+ overall_path_pruning_envelope_width;
254 			    }
255 			    // can prune here, although score_cutoff will
256 			    // be generally too generous at this point.
257 			    // It's unclear whether this pruning takes more
258 			    // time than it saves !
259 			    if(!do_pruning||betterthan(np->score,score_cutoff))
260 				vit_add_paths(p->next,np);
261 			    else
262 				delete np;
263 			}
264 		    }
265 	    }
266 
267 	    if (do_pruning)
268 	    {
269 		if(big_is_good)
270 		    score_cutoff =
271 			best_score - overall_path_pruning_envelope_width;
272 		else
273 		    score_cutoff =
274 			best_score + overall_path_pruning_envelope_width;
275 		if(trace)
276 		{
277 		    cerr << "Considered " << cands_considered << " of ";
278 		    cerr << cand_count*p->num_states << " candidate paths" << endl;
279 		    cerr << "FRAME: best score " << best_score;
280 		    cerr << "  score cutoff " << score_cutoff << endl;
281 		    cerr << "       best candidate score " << best_candidate_score;
282 		    cerr << "  candidate cutoff " << candidate_cutoff << endl;
283 		}
284 
285 		dcount=0; pcount=0;
286 		for (i=0; i<p->next->num_states; i++)
287 		    if(p->next->st_paths[i] != 0)
288 		    {
289 			pcount++;
290 			if(!betterthan(p->next->st_paths[i]->score,
291 				       score_cutoff))
292 			{
293 			    delete p->next->st_paths[i];
294 			    p->next->st_paths[i] = 0;
295 			    dcount++;
296 			}
297 		    }
298 		if(trace)
299 		{
300 		    cerr << "Pruned " << dcount << " of " << pcount;
301 		    cerr << " paths" << endl << endl;
302 		}
303 	    }
304 	}
305 	else                  // general beam search
306 	    for (t=p->paths; t != 0; t=t->next)
307 	    {   // for each path up to here
308 		for (c=p->cands; c != 0; c=c->next)
309 		{   // for each new candidate
310 		    np = (*user_npath)(t,c,f);
311 		    add_path(p->next,np);      // be a little cleverer
312 		}
313 	    }
314 	if (debug) fprintf(stdout,"\n");
315     }
316 
317 }
318 
319 void EST_Viterbi_Decoder::
prune_initialize(EST_VTPoint * p,double & best_score,double & best_candidate_score,double & score_cutoff,double & candidate_cutoff,int & cand_count)320      prune_initialize(EST_VTPoint *p,
321 		      double &best_score, double &best_candidate_score,
322 		      double &score_cutoff, double &candidate_cutoff,
323 		      int &cand_count)
324 {   // Find best candidate, count them and set some vars.
325     EST_VTCandidate *c;
326     if (big_is_good)
327     {
328 	best_score = -vit_a_big_number;
329 	best_candidate_score = -vit_a_big_number;
330 	score_cutoff = -vit_a_big_number;
331 	candidate_cutoff = - candidate_pruning_envelope_width;
332     }
333     else
334     {
335 	best_candidate_score = vit_a_big_number;
336 	best_score = vit_a_big_number;
337 	score_cutoff = vit_a_big_number;
338 	candidate_cutoff = candidate_pruning_envelope_width;
339     }
340 
341     for (cand_count=0,c=p->cands; c; c=c->next,cand_count++)
342 	if (betterthan(c->score,best_candidate_score))
343 	    best_candidate_score = c->score;
344     candidate_cutoff += best_candidate_score;
345 }
346 
debug_output_1(EST_VTPoint * p,EST_VTCandidate * c,EST_VTPath * np,int i)347 static void debug_output_1(EST_VTPoint *p,EST_VTCandidate *c,
348 			   EST_VTPath *np,int i)
349 {
350     printf("%s: ",(const char *)p->s->name());
351     cout << c->name;
352     printf(" %1.3f B %1.3f (%1.3f) st %d s %1.3f ",
353 	   np->c->score,
354 	   (np->c->score==0 ? 0 :
355 	    ((float)np->f("lscore"))/np->c->score),
356 	   (float)np->f("lscore"),np->state,
357 	   np->score);
358     if (p->st_paths[i] == 0)
359 	cout << "(I)" << endl;
360     else
361 	cout << p->st_paths[i]->c->name << endl;
362 }
363 
vit_add_paths(EST_VTPoint * pt,EST_VTPath * np)364 void EST_Viterbi_Decoder::vit_add_paths(EST_VTPoint *pt, EST_VTPath *np)
365 {
366     // Add set of paths
367     EST_VTPath *p,*next_p;
368 
369     for (p=np; p != 0; p=next_p)
370     {
371 	next_p = p->next;  // need to save this as p could be deleted
372 	vit_add_path(pt,p);
373     }
374 
375 }
vit_add_path(EST_VTPoint * p,EST_VTPath * np)376 void EST_Viterbi_Decoder::vit_add_path(EST_VTPoint *p, EST_VTPath *np)
377 {
378     // We are doing true viterbi so we need only keep the best
379     // path for each state.  This means we can index into the
380     // array of paths ending at P and only keep np if its score
381     // is better than any other path of that state
382 
383     if ((np->state < 0) || (np->state > p->num_states))
384     {
385 	cerr << "EST_Viterbi: state too big (" << np->state << ")" << endl;
386     }
387     else if ((p->st_paths[np->state] == 0) ||
388 	     (betterthan(np->score,p->st_paths[np->state]->score)))
389     {
390 	// This new one is better so replace it
391 	if (p->st_paths[np->state] != 0)
392 	    delete p->st_paths[np->state];
393 	p->st_paths[np->state] = np;
394     }
395     else
396 	delete np;
397 }
398 
vit_prune_path(double path_score,double score_cutoff)399 bool EST_Viterbi_Decoder::vit_prune_path(double path_score, double score_cutoff)
400 {
401 
402     // a bit of a simple function !!
403 
404     // if it falls below cutoff, then prune point
405     // typically will only be one path at this point anyway (Viterbi)
406     if(!betterthan(path_score,score_cutoff))
407 	return TRUE;
408 
409     return FALSE;
410 }
411 
412 
413 
add_path(EST_VTPoint * p,EST_VTPath * np)414 void EST_Viterbi_Decoder::add_path(EST_VTPoint *p, EST_VTPath *np)
415 {
416     // Add new path to point,  Prunes as appropriate to strategy
417     EST_VTPath *pp;
418 
419     if (p == 0)
420 	cerr << "Viterbi: tried to add path to NULL point\n";
421     else
422     {
423 	if ((beam_width == 0) ||            // Add if no beam restrictions or
424 	    (p->num_paths < beam_width) ||  //        beam not filled  or
425 	    (betterthan(np->score,p->paths->score)))//this is better than best
426 //	    (np->score > p->paths->score))  //        this is better than best
427 	{
428 	    EST_VTPath **l = &p->paths;
429 	    EST_VTPath *a;
430 
431 	    for (a=p->paths; /* scary */ ; a=a->next)
432 	    {
433 		if ((a == 0) || (betterthan(a->score,np->score)))
434 		{   // Add new path here
435 		    np->next = a;
436 		    *l = np;
437 		    p->num_paths++;
438 		    break;
439 		}
440 		l = &a->next;
441 	    }
442 	    // Prune the first one of the list
443 	    if ((beam_width > 0) &&
444 		(p->num_paths > beam_width))
445 	    {
446 		pp = p->paths;
447 		p->paths = pp->next;
448 		pp->next = 0;
449 		p->num_paths--;
450 		delete pp;
451 	    }
452 	}
453 	else
454 	{
455 	    delete np;  // failed to make it
456 	}
457     }
458 
459 }
460 
add_cand_prune(EST_VTCandidate * newcand,EST_VTCandidate * allcands)461 EST_VTCandidate *EST_Viterbi_Decoder::add_cand_prune(EST_VTCandidate *newcand,
462 					     EST_VTCandidate *allcands)
463 {
464     // Add newcand to allcand, in score order and prune it to
465     // cand_width, delete newcand if its not good enough
466     EST_VTCandidate *newlist = allcands;
467     EST_VTCandidate *pp;
468     int numcands;
469 
470     if (allcands == 0)
471 	numcands = 0;
472     else
473 	numcands = allcands->pos;
474 
475     if ((cand_width == 0) ||	// Add if no candbeam restrictions or
476 	(numcands < cand_width) || //        candbeam not filled  or
477 	(betterthan(newcand->score,allcands->score))) //this better than best
478     {
479 	EST_VTCandidate **l = &newlist;
480 	EST_VTCandidate *a;
481 
482 	for (a=newlist;		/* scary */ ; a=a->next)
483 	{
484 	    if ((a == 0) || (betterthan(a->score,newcand->score)))
485 	    {			// Add new path here
486 		newcand->next = a;
487 		*l = newcand;
488 		numcands++;
489 		break;
490 	    }
491 	    l = &a->next;
492 	}
493 	// Prune the first one off the list
494 	if ((cand_width > 0) &&
495 	    (numcands > cand_width))
496 	{
497 	    pp = newlist;
498 	    newlist = pp->next;
499 	    pp->next = 0;
500 	    numcands--;
501 	    delete pp;
502 	}
503     }
504     else
505 	delete newcand;		// failed to make it
506 
507     newlist->pos = numcands;
508     return newlist;
509 }
510 
result(const EST_String & n)511 bool EST_Viterbi_Decoder::result(const EST_String &n)
512 {
513     // Finds best path through the search space (previously searched)
514     // Adds field to the EST_Items, named n, with chosen value
515     EST_VTPath *p;
516 
517     if ((timeline == 0) || (timeline->next == 0))
518 	return TRUE;  // it's an empty list so it has succeeded
519     p = find_best_end();
520     if (p == 0)
521 	return FALSE; // there isn't any answer
522 
523     for (; p != 0; p=p->from)
524     {
525 	// Hmm need the original EST_Item
526 	if (p->c != 0)
527 	{
528 	    p->c->s->set_val(n,p->c->name);
529 	    p->c->s->set(n+"_score",p->f.F("lscore",0.0));
530 	}
531     }
532     return TRUE;
533 }
534 
result(EST_VTPath ** bestPathEnd)535 bool EST_Viterbi_Decoder::result( EST_VTPath **bestPathEnd )
536 {
537   // Finds best path through the search space (previously searched)
538   *bestPathEnd = 0;
539 
540   if ((timeline == 0) || (timeline->next == 0))
541     return TRUE;  // it's an empty list so it has succeeded
542 
543   *bestPathEnd = find_best_end();
544 
545   if (*bestPathEnd == 0)
546     return FALSE; // there isn't any answer
547 
548   return TRUE;
549 }
550 
551 
copy_feature(const EST_String & n)552 void EST_Viterbi_Decoder::copy_feature(const EST_String &n)
553 {
554     // Copy feature from path to related stream
555     EST_VTPath *p;
556 
557     p = find_best_end();
558     if(p == 0)
559 	return;
560 
561     for (; p != 0; p=p->from)
562     {
563 	// Hmm need the original EST_Item
564 	if ((p->c != 0) && (p->f.present(n)))
565 	    p->c->s->set_val(n,p->f(n));
566     }
567 }
568 
find_best_end() const569 EST_VTPath *EST_Viterbi_Decoder::find_best_end() const
570 {
571     EST_VTPoint *t;
572     double best,worst;
573     EST_VTPath *p, *best_p=0;
574     int i;
575     // I'd like to use HUGE_VAL or FLT_MAX for this but its not portable
576     // (on alphas)
577 
578     if (big_is_good)
579 	worst = -vit_a_big_number;	// worst possible;
580     else
581 	worst = vit_a_big_number;	// worst possible;
582     best = worst;		// hopefully we can find something better;
583 
584     for (i=0,t=timeline; t->next != 0; t=t->next,i++)
585     {
586 	if ((t->num_states == 0) && (t->num_paths == 0))
587 	{
588 	    cerr << "No paths at frame " << i << " " << t->s->name() << endl;
589 	    return 0;
590 	}
591     }
592 
593     if (num_states != 0)
594 	for (i=0; i<t->num_states; i++)
595 	{
596 	    if ((t->st_paths[i] != 0) &&
597 		(betterthan(t->st_paths[i]->score,best)))
598 	    {
599 		best = t->st_paths[i]->score;
600 		best_p = t->st_paths[i];
601 	    }
602 	}
603     else
604 	for (p=t->paths; p!=0; p=p->next)
605 	{
606 	    if (betterthan(p->score,best))
607 	    {
608 		best = p->score;
609 		best_p = p;
610 	    }
611 	}
612 
613 
614     if (debug)
615     {
616 	if (best == worst)
617 	    cerr << "Failed to find path" << endl;
618 	cout << "Best score is " << best << endl;
619     }
620 
621     return best_p;
622 }
623 
624