Julius 4.2
libjulius/src/graphout.c
説明を見る。
00001 
00018 /*
00019  * Copyright (c) 1991-2011 Kawahara Lab., Kyoto University
00020  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
00021  * Copyright (c) 2005-2011 Julius project team, Nagoya Institute of Technology
00022  * All rights reserved
00023  */
00024 
00025 #include <julius/julius.h>
00026 
00028 #undef GDEBUG
00029 
00031 #undef GDEBUG2
00032 
00033 #if defined(GDEBUG) || defined(GDEBUG2)
00034 static WCHMM_INFO *wchmm_local; 
00035 #endif
00036 
00053 void
00054 wordgraph_init(WCHMM_INFO *wchmm)
00055 {
00056 #if defined(GDEBUG) || defined(GDEBUG2)
00057   wchmm_local = wchmm;
00058 #endif
00059 }
00060 
00061 
00062 /**************************************************************/
00063 /* allocation and free of a WordGraph instance */
00064 
00101 static WordGraph *
00102 wordgraph_new(WORD_ID wid, HMM_Logical *headphone, HMM_Logical *tailphone, int leftframe, int rightframe, LOGPROB fscore_head, LOGPROB fscore_tail, LOGPROB gscore_head, LOGPROB gscore_tail, LOGPROB lscore, LOGPROB cm)
00103 {
00104   WordGraph *new;
00105 
00106   new = (WordGraph *)mymalloc(sizeof(WordGraph));
00107   new->wid = wid;
00108   new->lefttime = leftframe;
00109   new->righttime = rightframe;
00110   new->fscore_head = fscore_head;
00111   new->fscore_tail = fscore_tail;
00112   new->gscore_head = gscore_head;
00113   new->gscore_tail = gscore_tail;
00114   new->lscore_tmp = lscore;             /* n-gram only */
00115 #ifdef CM_SEARCH
00116   new->cmscore = cm;
00117 #endif
00118   new->forward_score = new->backward_score = 0.0;
00119   if (rightframe - leftframe + 1 != 0) {
00120     //new->amavg = (gscore_head - gscore_tail - lscore) / (float)(rightframe - leftframe + 1);
00121     new->amavg = (gscore_head - gscore_tail) / (float)(rightframe - leftframe + 1);
00122   }
00123   new->headphone = headphone;
00124   new->tailphone = tailphone;
00125   new->leftwordmaxnum = FANOUTSTEP;
00126   new->leftword = (WordGraph **)mymalloc(sizeof(WordGraph *) * new->leftwordmaxnum);
00127   new->left_lscore = (LOGPROB *)mymalloc(sizeof(LOGPROB) * new->leftwordmaxnum);
00128   new->leftwordnum = 0;
00129   new->rightwordmaxnum = FANOUTSTEP;
00130   new->rightword = (WordGraph **)mymalloc(sizeof(WordGraph *) * new->rightwordmaxnum);
00131   new->right_lscore = (LOGPROB *)mymalloc(sizeof(LOGPROB) * new->rightwordmaxnum);
00132   new->rightwordnum = 0;
00133 
00134   new->mark = FALSE;
00135 #ifdef GRAPHOUT_DYNAMIC
00136   new->purged = FALSE;
00137 #endif
00138   new->next = NULL;
00139   new->saved = FALSE;
00140 
00141   new->graph_cm = 0.0;
00142 
00143 #ifdef GDEBUG
00144  {
00145    int i;
00146    WordGraph *w;
00147    jlog("DEBUG: NEW: \"%s\"[%d..%d]\n", wchmm_local->winfo->woutput[new->wid], new->lefttime, new->righttime);
00148    for(i=0;i<new->leftwordnum;i++) {
00149      w = new->leftword[i];
00150      jlog("DEBUG: \t left%d:  \"%15s\"[%d..%d]\n", i, wchmm_local->winfo->woutput[w->wid], w->lefttime, w->righttime);
00151    }
00152    for(i=0;i<new->rightwordnum;i++) {
00153      w = new->rightword[i];
00154      jlog("DEBUG: \tright%d:  \"%15s\"[%d..%d]\n", i, wchmm_local->winfo->woutput[w->wid], w->lefttime, w->righttime);
00155    }
00156    jlog("DEBUG: \headphone: %s\n", new->headphone->name);
00157    jlog("DEBUG: \tailphone: %s\n", new->tailphone->name);
00158  }
00159 #endif
00160 
00161   return(new);
00162 }
00163 
00180 void
00181 wordgraph_free(WordGraph *wg)
00182 {
00183   free(wg->rightword);
00184   free(wg->right_lscore);
00185   free(wg->leftword);
00186   free(wg->left_lscore);
00187   free(wg);
00188 }
00189 
00190 /**************************************************************/
00191 /* Handling contexts */
00192 
00209 static void
00210 wordgraph_add_leftword(WordGraph *wg, WordGraph *left, LOGPROB lscore)
00211 {
00212   if (wg == NULL) return;
00213   if (left == NULL) return;
00214   if (wg->leftwordnum >= wg->leftwordmaxnum) {
00215     /* expand */
00216     wg->leftwordmaxnum += FANOUTSTEP;
00217     wg->leftword = (WordGraph **)myrealloc(wg->leftword, sizeof(WordGraph *) * wg->leftwordmaxnum);
00218     wg->left_lscore = (LOGPROB *)myrealloc(wg->left_lscore, sizeof(LOGPROB) * wg->leftwordmaxnum);
00219   }
00220   wg->leftword[wg->leftwordnum] = left;
00221   wg->left_lscore[wg->leftwordnum] = lscore;
00222   wg->leftwordnum++;
00223 #ifdef GDEBUG
00224   jlog("DEBUG: addleft: \"%s\"[%d..%d] added as %dth left of \"%s\"[%d..%d]\n", wchmm_local->winfo->woutput[left->wid], left->lefttime, left->righttime, wg->leftwordnum, wchmm_local->winfo->woutput[wg->wid], wg->lefttime, wg->righttime);
00225 #endif
00226 }
00227 
00246 static void
00247 wordgraph_add_rightword(WordGraph *wg, WordGraph *right, LOGPROB lscore)
00248 {
00249   if (wg == NULL) return;
00250   if (right == NULL) return;
00251   if (wg->rightwordnum >= wg->rightwordmaxnum) {
00252     /* expand */
00253     wg->rightwordmaxnum += FANOUTSTEP;
00254     wg->rightword = (WordGraph **)myrealloc(wg->rightword, sizeof(WordGraph *) * wg->rightwordmaxnum);
00255     wg->right_lscore = (LOGPROB *)myrealloc(wg->right_lscore, sizeof(LOGPROB) * wg->rightwordmaxnum);
00256   }
00257   wg->rightword[wg->rightwordnum] = right;
00258   wg->right_lscore[wg->rightwordnum] = lscore;
00259   wg->rightwordnum++;
00260 #ifdef GDEBUG
00261   jlog("DEBUG: addright: \"%s\"[%d..%d] added as %dth right of \"%s\"[%d..%d]\n", wchmm_local->winfo->woutput[right->wid], right->lefttime, right->righttime, wg->rightwordnum, wchmm_local->winfo->woutput[wg->wid], wg->lefttime, wg->righttime);
00262 #endif
00263 }
00264 
00294 boolean
00295 wordgraph_check_and_add_leftword(WordGraph *wg, WordGraph *left, LOGPROB lscore)
00296 {
00297   int i;
00298 
00299   if (wg == NULL) return FALSE;
00300   if (left == NULL) return FALSE;
00301   for(i=0;i<wg->leftwordnum;i++) {
00302     if (wg->leftword[i] == left) {
00303       break;
00304     }
00305   }
00306   if (i >= wg->leftwordnum) { /* no leftword matched */
00307     wordgraph_add_leftword(wg, left, lscore);
00308     return TRUE;
00309   } else if (wg->left_lscore[i] < lscore) {
00310     /* for same word connection, keep maximum LM score */
00311     if (debug2_flag) jlog("DEBUG: check_and_add_leftword: update left\n");
00312     wg->left_lscore[i] = lscore;
00313   }
00314   return FALSE;
00315 }
00316 
00346 boolean
00347 wordgraph_check_and_add_rightword(WordGraph *wg, WordGraph *right, LOGPROB lscore)
00348 {
00349   int i;
00350 
00351   if (wg == NULL) return FALSE;
00352   if (right == NULL) return FALSE;
00353   for(i=0;i<wg->rightwordnum;i++) {
00354     if (wg->rightword[i] == right) {
00355       break;
00356     }
00357   }
00358   if (i >= wg->rightwordnum) { /* no rightword matched */
00359     wordgraph_add_rightword(wg, right, lscore);
00360     return TRUE;
00361   } else if (wg->right_lscore[i] < lscore) {
00362     /* for same word connection, keep maximum LM score */
00363     if (debug2_flag) jlog("DEBUG: check_and_add_rightword: update right\n");
00364     wg->right_lscore[i] = lscore;
00365   }
00366   return FALSE;
00367 }
00368 
00389 static boolean
00390 merge_contexts(WordGraph *dst, WordGraph *src)
00391 {
00392   int s, d;
00393   WordGraph *adding;
00394   boolean ret;
00395 
00396 #ifdef GDEBUG
00397   jlog("DEBUG: merge_contexts: merging context of \"%s\"[%d..%d] to \"%s\"[%d..%d]...\n",
00398          wchmm_local->winfo->woutput[src->wid], src->lefttime, src->righttime,
00399          wchmm_local->winfo->woutput[dst->wid], dst->lefttime, dst->righttime);
00400 #endif
00401 
00402   ret = FALSE;
00403   
00404   /* left context */
00405   for(s=0;s<src->leftwordnum;s++) {
00406     adding = src->leftword[s];
00407     if (adding->mark) continue;
00408     /* direct link between dst and src will disapper to avoid unneccesary loop */
00409     if (adding == dst) {
00410 #ifdef GDEBUG
00411       jlog("DEBUG: merge_contexts: skipping direct link (dst) -> (src)\n");
00412 #endif
00413       continue;
00414     }
00415     for(d=0;d<dst->leftwordnum;d++) {
00416       if (dst->leftword[d]->mark) continue;
00417       if (dst->leftword[d] == adding) {
00418         break;
00419       }
00420     }
00421     if (d >= dst->leftwordnum) { /* no leftword matched */
00422       wordgraph_add_leftword(dst, adding, src->left_lscore[s]);
00423 #ifdef GDEBUG
00424       jlog("DEBUG: merge_contexts: added \"%s\"[%d..%d] as a new left context\n",
00425              wchmm_local->winfo->woutput[adding->wid], adding->lefttime, adding->righttime);
00426 #endif
00427       ret = TRUE;
00428     } else if (dst->left_lscore[d] < src->left_lscore[s]) {
00429       jlog("DEBUG: merge_context: update left\n");
00430       dst->left_lscore[d] = src->left_lscore[s];
00431     }
00432 #ifdef GDEBUG
00433     else {
00434       jlog("DEBUG: merge_contexts: \"%s\"[%d..%d] already exist\n",
00435              wchmm_local->winfo->woutput[adding->wid], adding->lefttime, adding->righttime);
00436     }
00437 #endif
00438   }
00439 
00440   /* right context */
00441   for(s=0;s<src->rightwordnum;s++) {
00442     adding = src->rightword[s];
00443     if (adding->mark) continue;
00444     /* direct link between dst and src will disapper to avoid unneccesary loop */
00445     if (adding == dst) {
00446 #ifdef GDEBUG
00447       jlog("DEBUG: merge_contexts: skipping direct link (src) -> (dst)\n");
00448 #endif
00449       continue;
00450     }
00451     for(d=0;d<dst->rightwordnum;d++) {
00452       if (dst->rightword[d]->mark) continue;
00453       if (dst->rightword[d] == adding) {
00454         break;
00455       }
00456     }
00457     if (d >= dst->rightwordnum) { /* no rightword matched */
00458       wordgraph_add_rightword(dst, adding, src->right_lscore[s]);
00459 #ifdef GDEBUG
00460       jlog("DEBUG: merge_contexts: added \"%s\"[%d..%d] as a new right context\n",
00461              wchmm_local->winfo->woutput[adding->wid], adding->lefttime, adding->righttime);
00462 #endif
00463       ret = TRUE;
00464     } else if (dst->right_lscore[d] < src->right_lscore[s]) {
00465       jlog("DEBUG: merge_context: update right\n");
00466       dst->right_lscore[d] = src->right_lscore[s];
00467     }
00468 #ifdef GDEBUG
00469     else {
00470       jlog("DEBUG: merge_contexts: \"%s\"[%d..%d] already exist\n",
00471              wchmm_local->winfo->woutput[adding->wid], adding->lefttime, adding->righttime);
00472     }
00473 #endif
00474   }
00475   
00476   return(ret);
00477 }
00478 
00497 static void
00498 swap_leftword(WordGraph *wg, WordGraph *from, WordGraph *to, LOGPROB lscore)
00499 {
00500   int i;
00501   
00502 #ifdef GDEBUG
00503   jlog("DEBUG: swapleft: replacing left of \"%s\"[%d..%d] from \"%s\"[%d..%d] to \"%s\"[%d..%d]...\n",
00504          wchmm_local->winfo->woutput[wg->wid], wg->lefttime, wg->righttime,
00505          wchmm_local->winfo->woutput[from->wid], from->lefttime, from->righttime,
00506          wchmm_local->winfo->woutput[to->wid], to->lefttime, to->righttime);
00507 #endif
00508   
00509   for(i=0;i<wg->leftwordnum;i++) {
00510     if (wg->leftword[i] == from) {
00511       wg->leftword[i] = to;
00512       wg->left_lscore[i] = lscore;
00513     }
00514   }
00515 }
00516 
00535 static void
00536 swap_rightword(WordGraph *wg, WordGraph *from, WordGraph *to, LOGPROB lscore)
00537 {
00538   int i;
00539   
00540 #ifdef GDEBUG
00541   jlog("DEBUG: swapright: replacing right of \"%s\"[%d..%d] from \"%s\"[%d..%d] to \"%s\"[%d..%d]...\n",
00542          wchmm_local->winfo->woutput[wg->wid], wg->lefttime, wg->righttime,
00543          wchmm_local->winfo->woutput[from->wid], from->lefttime, from->righttime,
00544          wchmm_local->winfo->woutput[to->wid], to->lefttime, to->righttime);
00545 #endif
00546 
00547   for(i=0;i<wg->rightwordnum;i++) {
00548     if (wg->rightword[i] == from) {
00549       wg->rightword[i] = to;
00550       wg->right_lscore[i] = lscore;
00551     }
00552   }
00553 }
00554 
00567 static void
00568 uniq_leftword(WordGraph *wg)
00569 {
00570   int i, j, dst;
00571   boolean ok;
00572 
00573   dst = 0;
00574   for(i=0;i<wg->leftwordnum;i++) {
00575     ok = TRUE;
00576     for(j=0;j<dst;j++) {
00577       if (wg->leftword[i] == wg->leftword[j]) {
00578         ok = FALSE;
00579         break;
00580       }
00581     }
00582     if (ok == TRUE) {
00583       wg->leftword[dst] = wg->leftword[i];
00584       wg->left_lscore[dst] = wg->left_lscore[i];
00585       dst++;
00586     }
00587   }
00588   wg->leftwordnum = dst;
00589 }
00590 
00603 static void
00604 uniq_rightword(WordGraph *wg)
00605 {
00606   int i, j, dst;
00607   boolean ok;
00608 
00609   dst = 0;
00610   for(i=0;i<wg->rightwordnum;i++) {
00611     ok = TRUE;
00612     for(j=0;j<dst;j++) {
00613       if (wg->rightword[i] == wg->rightword[j]) {
00614         ok = FALSE;
00615         break;
00616       }
00617     }
00618     if (ok == TRUE) {
00619       wg->rightword[dst] = wg->rightword[i];
00620       wg->right_lscore[dst] = wg->right_lscore[i];
00621       dst++;
00622     }
00623   }
00624   wg->rightwordnum = dst;
00625 }
00626 
00639 static void
00640 wordgraph_remove_context(WordGraph *wg)
00641 {
00642   WordGraph *w;
00643   int i,j,k;
00644 
00645   if (wg == NULL) return;
00646 
00647   for(i=0;i<wg->leftwordnum;i++) {
00648     w = wg->leftword[i];
00649     k=0;
00650     for(j=0;j<w->rightwordnum;j++) {
00651       if (w->rightword[j] != wg) {
00652         if (j != k) {
00653           w->rightword[k] = w->rightword[j];
00654           w->right_lscore[k] = w->right_lscore[j];
00655         }
00656         k++;
00657       }
00658     }
00659     w->rightwordnum = k;
00660   }
00661   for(i=0;i<wg->rightwordnum;i++) {
00662     w = wg->rightword[i];
00663     k=0;
00664     for(j=0;j<w->leftwordnum;j++) {
00665       if (w->leftword[j] != wg) {
00666         if (j != k) {
00667           w->leftword[k] = w->leftword[j];
00668           w->left_lscore[k] = w->left_lscore[j];
00669         }
00670         k++;
00671       }
00672     }
00673     w->leftwordnum = k;
00674 #ifdef GDEBUG2
00675     if (w->leftwordnum == 0) {
00676       jlog("DEBUG: leftword becomes 0 by remove_context\n");
00677       put_wordgraph(jlog_get_fp(), w, wchmm_local->winfo);
00678       jlog("DEBUG: by deleting its left context:\n");
00679       put_wordgraph(jlog_get_fp(), wg, wchmm_local->winfo);
00680     }
00681 #endif
00682   }
00683 }
00684 
00697 static void
00698 wordgraph_link_context(WordGraph *wg)
00699 {
00700   int i,j;
00701   WordGraph *left, *right;
00702   
00703   if (wg == NULL) return;
00704   for(i=0;i<wg->leftwordnum;i++) {
00705     left = wg->leftword[i];
00706     if (left->mark) continue;
00707     if (left == wg) continue;
00708     for(j=0;j<wg->rightwordnum;j++) {
00709       right = wg->rightword[j];
00710       if (right->mark) continue;
00711       if (right == wg) continue;
00712       if (left == right) continue;
00713       wordgraph_check_and_add_leftword(right, left, wg->left_lscore[i]);
00714       wordgraph_check_and_add_rightword(left, right, wg->right_lscore[j]);
00715     }
00716   }
00717 }
00718 
00719 /**************************************************************/
00720 /* Operations for organizing WordGraph set */
00721 
00738 static int
00739 wordgraph_exec_erase(WordGraph **rootp)
00740 {
00741   WordGraph *wg, *we, *wtmp;
00742   int count;
00743 
00744   if (*rootp == NULL) return(0);
00745   
00746   wg = *rootp;
00747   count = 0;
00748   while (wg != NULL) {
00749     we = wg->next;
00750     while(we != NULL && we->mark == TRUE) {
00751       wtmp = we->next;
00752       wordgraph_free(we); count++;
00753       we = wtmp;
00754     }
00755     wg->next = we;
00756     wg = we;
00757   }
00758   if ((*rootp)->mark == TRUE) {
00759     wtmp = (*rootp)->next;
00760     wordgraph_free(*rootp); count++;
00761     *rootp = wtmp;
00762   }
00763 
00764   return(count);
00765 }
00766 
00785 static int
00786 compare_lefttime(WordGraph **x, WordGraph **y)
00787 {
00788   if ((*x)->lefttime > (*y)->lefttime) return 1;
00789   else if ((*x)->lefttime < (*y)->lefttime) return -1;
00790   else {
00791     if ((*x)->righttime > (*y)->righttime) return 1;
00792     else if ((*x)->righttime < (*y)->righttime) return -1;
00793     else {
00794       if ((*x)->fscore_head < (*y)->fscore_head) return 1;
00795       else if ((*x)->fscore_head > (*y)->fscore_head) return -1;
00796       else return 0;
00797     }
00798   }
00799 }
00800 
00819 int
00820 wordgraph_sort_and_annotate_id(WordGraph **rootp, RecogProcess *r)
00821 {
00822   WordGraph *wg;
00823   int cnt;
00824   WordGraph **wlist;
00825   int i;
00826   WordGraph *wo;
00827 
00828   /* count total number of words in the graph */
00829   cnt = 0;
00830   for(wg=*rootp;wg;wg=wg->next) cnt++;
00831   if (cnt == 0) return 0;
00832   /* sort them by lefttime */
00833   wlist = (WordGraph **)mymalloc(sizeof(WordGraph *) * cnt);
00834   i = 0;
00835   for(wg=*rootp;wg;wg=wg->next) {
00836     wlist[i++] = wg;
00837   }
00838   qsort(wlist, cnt, sizeof(WordGraph *), (int (*)(const void *, const void *))compare_lefttime);
00839 
00840   /* annotated id and re-order the link by the id */
00841   wo = NULL;
00842   for(i=cnt-1;i>=0;i--) {
00843     wg = wlist[i];
00844     wg->id = i;
00845     wg->next = wo;
00846     wo = wg;
00847   }
00848   *rootp = wo;
00849   free(wlist);
00850 
00851   return cnt;
00852 }
00853 
00870 void
00871 wordgraph_clean(WordGraph **rootp)
00872 {
00873   WordGraph *wg, *wtmp;
00874 
00875   wg = *rootp;
00876   while(wg != NULL) {
00877     wtmp = wg->next;
00878     wordgraph_free(wg);
00879     wg = wtmp;
00880   }
00881   *rootp = NULL;
00882 
00883 }
00884 
00885 /*********************************************************************/
00886 /* Post-processing of generated word arcs after search has been done */
00887 
00908 static int
00909 compare_beam(WordGraph **x, WordGraph **y)
00910 {
00911   if ((*x)->fscore_head < (*y)->fscore_head) return 1;
00912   else if ((*x)->fscore_head > (*y)->fscore_head) return -1;
00913   else return 0;
00914 }
00915 
00940 void
00941 wordgraph_purge_leaf_nodes(WordGraph **rootp, RecogProcess *r)
00942 {
00943   WordGraph *wg;
00944   int i, dst;
00945   boolean changed;
00946   int count, erased, del_left, del_right;
00947 
00948   /* count whole */
00949   count = 0;
00950   for(wg=*rootp;wg;wg=wg->next) count++;
00951   if (verbose_flag) jlog("STAT: graphout: %d initial word arcs generated\n", count);
00952   if (count == 0) return;
00953   
00954   if (verbose_flag) jlog("STAT: graphout: step 1: purge leaf nodes\n");
00955 
00956   /* mark words to be erased */
00957   del_left = del_right = 0;
00958   do {
00959     changed = FALSE;
00960     for(wg=*rootp;wg;wg=wg->next) {
00961       if (wg->mark == TRUE) continue;
00962       /* mark if wg has no left context, or all leftwords are marked */
00963       if (wg->lefttime != 0) {
00964         for(i=0;i<wg->leftwordnum;i++) {
00965           if (wg->leftword[i]->mark == FALSE) break;
00966         }
00967         if (i >= wg->leftwordnum) {
00968           wg->mark = TRUE;
00969           changed = TRUE;
00970           del_left++;
00971           continue;
00972         }
00973       }
00974       /* mark if wg has no right context, or all rightwords are marked */
00975       if (wg->righttime != r->peseqlen - 1) {
00976         for(i=0;i<wg->rightwordnum;i++) {
00977           if (wg->rightword[i]->mark == FALSE) break;
00978         }
00979         if (i >= wg->rightwordnum) {
00980           wg->mark = TRUE;
00981           changed = TRUE;
00982           del_right++;
00983           continue;
00984         }
00985       }
00986     }
00987   } while (changed == TRUE);
00988 
00989   if (verbose_flag) jlog("STAT: graphout: %d leaf words found (left_blank=%d, right_blank=%d)\n", del_left + del_right, del_left, del_right);
00990 
00991   /* do compaction of left/rightwords */
00992     for(wg=*rootp;wg;wg=wg->next) {
00993       if (wg->mark) continue;
00994       dst = 0;
00995       for(i=0;i<wg->leftwordnum;i++) {
00996         if (wg->leftword[i]->mark == FALSE) {
00997           if (dst != i) {
00998             wg->leftword[dst] = wg->leftword[i];
00999             wg->left_lscore[dst] = wg->left_lscore[i];
01000           }
01001           dst++;
01002         }
01003       }
01004       wg->leftwordnum = dst;
01005     }
01006     for(wg=*rootp;wg;wg=wg->next) {
01007       if (wg->mark) continue;
01008       dst = 0;
01009       for(i=0;i<wg->rightwordnum;i++) {
01010         if (wg->rightword[i]->mark == FALSE) {
01011           if (dst != i) {
01012             wg->rightword[dst] = wg->rightword[i];
01013             wg->right_lscore[dst] = wg->right_lscore[i];
01014           }
01015           dst++;
01016         }
01017       }
01018       wg->rightwordnum = dst;
01019     }
01020 
01021   /* execute erase of marked words */
01022   erased = wordgraph_exec_erase(rootp);
01023   if (verbose_flag) jlog("STAT: graphout: %d words purged, %d words left in lattice\n", erased, count - erased);
01024 
01025 }
01026 
01049 void
01050 wordgraph_depth_cut(WordGraph **rootp, RecogProcess *r)
01051 {
01052 #ifdef GRAPHOUT_DEPTHCUT
01053 
01054   WordGraph *wg;
01055   int i, dst;
01056   boolean changed;
01057   int count, erased, del_left, del_right;
01058   WordGraph **wlist;
01059   boolean f;
01060   int *wc;
01061   int t;
01062   int pruned;
01063 
01064 
01065   if (r->config->graph.graphout_cut_depth < 0) return;
01066 
01067   if (verbose_flag) jlog("STAT: graphout: step 1.5: cut less likely hypothesis by depth of %d\n", r->config->graph.graphout_cut_depth);
01068 
01069   /* count whole */
01070   count = 0;
01071   for(wg=*rootp;wg;wg=wg->next) count++;
01072   if (count == 0) return;
01073   
01074   /* prepare buffer to count words per frame */
01075   wc = (int *)mymalloc(sizeof(int) * r->peseqlen);
01076   for (t=0;t<r->peseqlen;t++) wc[t] = 0;
01077   /* sort words by fscore_head */
01078   wlist = (WordGraph **)mymalloc(sizeof(WordGraph *) * count);
01079   i = 0;
01080   for(wg=*rootp;wg;wg=wg->next) {
01081     wlist[i++] = wg;
01082   }
01083   qsort(wlist, count, sizeof(WordGraph *), (int (*)(const void *, const void *))compare_beam);
01084   /* count words per frame, and unlink/mark them if below beam width */
01085   pruned = 0;
01086   for (i=0;i<count;i++) {
01087     wg = wlist[i];
01088     f = TRUE;
01089     for (t=wg->lefttime;t<=wg->righttime;t++) {
01090       wc[t]++;
01091       if (wc[t] <= r->config->graph.graphout_cut_depth) f = FALSE;
01092     }
01093     if (f) {
01094       //wordgraph_remove_context(wg);
01095       wg->mark = TRUE;
01096       pruned++;
01097     }
01098   }
01099 #ifdef GDEBUG2
01100   jlog("DEBUG: GRAPH DEPTH STATISTICS: NUMBER OF WORDS PER FRAME\n");
01101   for(t=0;t<r->peseqlen;t++) {
01102     if (wc[t] > r->config->graph.graphout_cut_depth) {
01103       jlog("*");
01104     } else {
01105       jlog(" ");
01106     }
01107     jlog("%4d: %d\n", t, wc[t]);
01108   }
01109 #endif
01110   if (verbose_flag) jlog("STAT: graphout: %d words out of %d are going to be pruned by depth cutting\n", pruned, count);
01111   free(wlist);
01112   free(wc);
01113 
01114   /* mark words to be erased */
01115   del_left = del_right = 0;
01116   do {
01117     changed = FALSE;
01118     for(wg=*rootp;wg;wg=wg->next) {
01119       if (wg->mark == TRUE) continue;
01120       /* mark if wg has no left context, or all leftwords are marked */
01121       if (wg->lefttime != 0) {
01122         for(i=0;i<wg->leftwordnum;i++) {
01123           if (wg->leftword[i]->mark == FALSE) break;
01124         }
01125         if (i >= wg->leftwordnum) {
01126           wg->mark = TRUE;
01127           changed = TRUE;
01128           del_left++;
01129           continue;
01130         }
01131       }
01132       /* mark if wg has no right context, or all rightwords are marked */
01133       if (wg->righttime != r->peseqlen - 1) {
01134         for(i=0;i<wg->rightwordnum;i++) {
01135           if (wg->rightword[i]->mark == FALSE) break;
01136         }
01137         if (i >= wg->rightwordnum) {
01138           wg->mark = TRUE;
01139           changed = TRUE;
01140           del_right++;
01141           continue;
01142         }
01143       }
01144     }
01145   } while (changed == TRUE);
01146 
01147   if (verbose_flag) jlog("STAT: graphout: %d new leaves found (left_blank=%d, right_blank=%d)\n", del_left + del_right, del_left, del_right);
01148 
01149   /* do compaction of left/rightwords */
01150     for(wg=*rootp;wg;wg=wg->next) {
01151       if (wg->mark) continue;
01152       dst = 0;
01153       for(i=0;i<wg->leftwordnum;i++) {
01154         if (wg->leftword[i]->mark == FALSE) {
01155           if (dst != i) {
01156             wg->leftword[dst] = wg->leftword[i];
01157             wg->left_lscore[dst] = wg->left_lscore[i];
01158           }
01159           dst++;
01160         }
01161       }
01162       wg->leftwordnum = dst;
01163     }
01164     for(wg=*rootp;wg;wg=wg->next) {
01165       if (wg->mark) continue;
01166       dst = 0;
01167       for(i=0;i<wg->rightwordnum;i++) {
01168         if (wg->rightword[i]->mark == FALSE) {
01169           if (dst != i) {
01170             wg->rightword[dst] = wg->rightword[i];
01171             wg->right_lscore[dst] = wg->right_lscore[i];
01172           }
01173           dst++;
01174         }
01175       }
01176       wg->rightwordnum = dst;
01177     }
01178 
01179   /* execute erase of marked words */
01180   erased = wordgraph_exec_erase(rootp);
01181   if (verbose_flag) jlog("STAT: graphout: total %d words purged, %d words left in lattice\n", erased, count - erased);
01182 
01183 #else  /* ~GRAPHOUT_DEPTHCUT */
01184 
01185   if (verbose_flag) jlog("STAT: graphout: step 1.5: graph depth cutting has been disabled, skipped\n");
01186 
01187 #endif
01188 
01189 }
01190 
01236 static boolean
01237 wordgraph_adjust_boundary_sub(WordGraph **rootp, int *mov_num_ret, int *dup_num_ret, int *del_num_ret, int *mod_num_ret, int count, int *maxfnum, int peseqlen, int lmtype, int **p_framelist, LOGPROB **p_framescorelist)
01238 {
01239   WordGraph *wg, *left, *new;
01240   int i, j, k;
01241   int fnum;
01242   int mov_num, dup_num, del_num, mod_num;
01243   boolean changed = FALSE;
01244   int *framelist;
01245   LOGPROB *framescorelist;
01246 
01247   mov_num = dup_num = del_num = mod_num = 0;
01248 
01249   framelist = *p_framelist;
01250   framescorelist = *p_framescorelist;
01251 
01252   /* maximum number of left context words does not exceed total word num */
01253   /* allocate temporal work area.  these are permanent buffer that will
01254      be kept between recognition sessions. */
01255   if (*maxfnum == 0) {
01256     /* when this is called for the first time, allocate buffer */
01257     *maxfnum = count;
01258     framelist = (int *)mymalloc(sizeof(int) * (*maxfnum));
01259     framescorelist = (LOGPROB *)mymalloc(sizeof(LOGPROB) * (*maxfnum));
01260 #ifdef GDEBUG
01261     jlog("DEBUG: Notice: maxfnum starts at %d\n", *maxfnum);
01262 #endif
01263   } else if (*maxfnum < count) {
01264     /* for later call, expand buffer if necessary */
01265     free(framescorelist);
01266     free(framelist);
01267     *maxfnum = count;
01268     framelist = (int *)mymalloc(sizeof(int) * (*maxfnum));
01269     framescorelist = (LOGPROB *)mymalloc(sizeof(LOGPROB) * (*maxfnum));
01270 #ifdef GDEBUG
01271     jlog("DEBUG: Notice: maxfnum expanded by count (%d)\n", *maxfnum);
01272 #endif
01273   }
01274 
01275 #ifdef GDEBUG2
01276   jlog("DEBUG: ***CHECK LOOP BEGIN***\n");
01277 #endif
01278   for(wg=*rootp;wg;wg=wg->next) {
01279     if (wg->mark) continue;     /* already marked */
01280 #ifdef GDEBUG2
01281     jlog("DEBUG:   [%d..%d] \"%s\"\n", wg->lefttime, wg->righttime, wchmm_local->winfo->woutput[wg->wid]);
01282 #endif
01283     if (wg->leftwordnum == 0) { /* no leftword */
01284       if (wg->lefttime != 0) {
01285         /* some fraction found by former elimination: remove this */
01286 #ifdef GDEBUG2
01287         jlog("DEBUG:   -> no leftword at middle of lattice, eliminate this\n");
01288 #endif
01289         wordgraph_remove_context(wg);
01290         wg->mark = TRUE;
01291         del_num++;
01292         changed = TRUE;
01293       }
01294       /* if has no leftword, adjustment of this word is not needed */
01295       continue;
01296     }
01297     if (wg->rightwordnum == 0) {        /* no rightword */
01298       if (wg->righttime != peseqlen-1) {
01299         /* some fraction found by former elimination: remove this */
01300 #ifdef GDEBUG2
01301         jlog("DEBUG:   -> no rightword at middle of lattice, eliminate this\n");
01302 #endif
01303         wordgraph_remove_context(wg);
01304         wg->mark = TRUE;
01305         del_num++;
01306         changed = TRUE;
01307         continue;
01308       }
01309       /* if on right edge, continue adjusting */
01310     }
01311     /* correct lefttime variation to framelist[] and framescorelist[] */
01312     fnum = 0;
01313     /* check for buffer overrun */
01314     if (wg->leftwordnum > (*maxfnum)) {
01315       /* expand buffer if necessary */
01316       free(framescorelist);
01317       free(framelist);
01318       *maxfnum = wg->leftwordnum;
01319       framelist = (int *)mymalloc(sizeof(int) * (*maxfnum));
01320       framescorelist = (LOGPROB *)mymalloc(sizeof(LOGPROB) * (*maxfnum));
01321 #ifdef GDEBUG
01322       jlog("DEBUG: Notice: wg->leftwordnum exceeds maxfnum (%d > %d), expanded\n", wg->leftwordnum, *maxfnum);
01323 #endif
01324     }
01325     for(i=0;i<wg->leftwordnum;i++) {
01326       left = wg->leftword[i];
01327       if (left->mark) continue;
01328       for(j=0;j<fnum;j++) {
01329         if (framelist[j] == left->righttime + 1) break;
01330       }
01331       if (j >= fnum) {
01332         framelist[fnum] = left->righttime + 1;
01333         /* the tail gscore contains the language score of the word,
01334            so the head gscore of its right context should consider this */
01335         framescorelist[fnum] = left->gscore_tail - wg->left_lscore[i];
01336         fnum++;
01337       }
01338     }
01339 #ifdef GDEBUG2
01340     jlog("DEBUG:   possible boundary of left words:");
01341     if (fnum == 0) {
01342       jlog(" (not exist)\n");
01343     } else {
01344       for(j=0;j<fnum;j++) jlog(" %d", framelist[j]);
01345       jlog("\n");
01346     }
01347 #endif
01348     if (fnum == 0) continue;    /* no left context */
01349     /* one candidate: just move the original (or not move) */
01350     if (fnum == 1) {
01351       if (wg->lefttime != framelist[0]) {
01352 #ifdef GDEBUG2
01353         jlog("DEBUG:     !moving as [%d..%d]", framelist[0], wg->righttime);
01354 #endif
01355         /* check the time correctness: if the lefttime is larger than
01356            righttime, this graph word has been completely overridden by
01357            the left word (i.e. the aligned frames are absorbed by
01358            re-alignment.  In this case this word should be removed.
01359         */
01360         if (framelist[0] > wg->righttime) {
01361 #ifdef GDEBUG2
01362           jlog(" : eliminated");
01363 #endif
01364           wordgraph_link_context(wg);
01365           wordgraph_remove_context(wg);
01366           wg->mark = TRUE;
01367           del_num++;
01368         } else {
01369 #ifdef GDEBUG2
01370           jlog(" : ok");
01371 #endif
01372           /* adjust time and score */
01373           wg->lefttime = framelist[0];
01374           wg->gscore_head = framescorelist[0];
01375           mov_num++;
01376         }
01377 #ifdef GDEBUG2
01378         jlog("\n");
01379 #endif
01380         changed = TRUE;
01381       } else if (wg->gscore_head != framescorelist[0]) {
01382         /* adjust only score */
01383 #ifdef GDEBUG2
01384         jlog("DEBUG:     !ghead score changed: %f -> %f\n", wg->gscore_head, framescorelist[0]);
01385 #endif
01386         wg->gscore_head = framescorelist[0];
01387         mod_num++;
01388         changed = TRUE;
01389       }
01390     }
01391     if (fnum > 1) {
01392       /* multiple candidate: make copy for each (fnum)*/
01393       for(j=0;j<fnum;j++) {
01394         /* duplicate */
01395         dup_num++;
01396 #ifdef GDEBUG2
01397         jlog("DEBUG:     !duping as [%d..%d]", framelist[j], wg->righttime);
01398 #endif
01399         
01400         if (framelist[j] > wg->righttime) {
01401           /* bogus link: link leftwords and rightwords, and delete this */
01402 #ifdef GDEBUG2
01403           jlog(" : eliminated");
01404 #endif
01405           for(i=0;i<wg->leftwordnum;i++) {
01406             left = wg->leftword[i];
01407             if (left->mark) continue;
01408             if (left->righttime + 1 == framelist[j]) {
01409               for(k=0;k<wg->rightwordnum;k++) {
01410                 if ((wg->rightword[k])->mark) continue;
01411                 if (wg->rightword[k] == left) continue;
01412                 wordgraph_check_and_add_leftword(wg->rightword[k], left, wg->left_lscore[i]);
01413                 wordgraph_check_and_add_rightword(left, wg->rightword[k], wg->right_lscore[k]);
01414               }
01415             }
01416           }
01417           del_num++;
01418           
01419         } else {
01420           /* really duplicate */
01421 #ifdef GDEBUG2
01422           jlog(" : ok");
01423 #endif
01424           new = wordgraph_new(wg->wid, wg->headphone, wg->tailphone, framelist[j], wg->righttime, wg->fscore_head, wg->fscore_tail, framescorelist[j], wg->gscore_tail, wg->lscore_tmp
01425 #ifdef CM_SEARCH
01426                               , wg->cmscore
01427 #else
01428                               , LOG_ZERO
01429 #endif
01430                               );
01431           /* copy corresponding link */
01432           for(i=0;i<wg->leftwordnum;i++) {
01433             if ((wg->leftword[i])->mark) continue;
01434             if ((wg->leftword[i])->righttime + 1 == framelist[j]) {
01435               wordgraph_add_leftword(new, wg->leftword[i], wg->left_lscore[i]);
01436               wordgraph_add_rightword(wg->leftword[i], new, wg->left_lscore[i]);
01437             }
01438           }
01439           for(i=0;i<wg->rightwordnum;i++) {
01440             if ((wg->rightword[i])->mark) continue;
01441             wordgraph_add_rightword(new, wg->rightword[i], wg->right_lscore[i]);
01442             wordgraph_add_leftword(wg->rightword[i], new, wg->right_lscore[i]);
01443           }
01444           new->saved = TRUE;
01445           new->next = *rootp;
01446           *rootp = new;
01447         }
01448 
01449 #ifdef GDEBUG2
01450         jlog("\n");
01451 #endif
01452       }
01453       
01454       /* remove the original */
01455 #ifdef GDEBUG2
01456       jlog("DEBUG:     !delete original [%d..%d]\n", wg->lefttime, wg->righttime);
01457 #endif
01458       wordgraph_remove_context(wg);
01459       wg->mark = TRUE;
01460       dup_num--;
01461       
01462       changed = TRUE;
01463     }
01464   }
01465 
01466   *mov_num_ret = mov_num;
01467   *dup_num_ret = dup_num;
01468   *del_num_ret = del_num;
01469   *mod_num_ret = mod_num;
01470 
01471   *p_framelist = framelist;
01472   *p_framescorelist = framescorelist;
01473 
01474 #ifdef GDEBUG2
01475   if (changed) {
01476     jlog("DEBUG: *** some graph has been altered, check loop continues\n");
01477   } else {
01478     jlog("DEBUG: *** graph not changed at last loop, check ends here\n");
01479   }
01480 #endif
01481 
01482   return (changed);
01483 }
01484 
01501 static void
01502 wordgraph_compaction_thesame_sub(WordGraph **rootp, int *rest_ret, int *merged_ret)
01503 {
01504   WordGraph *wg, *we;
01505   int i, count, erased, merged;
01506 
01507   count = 0;
01508   merged = 0;
01509   for(wg=*rootp;wg;wg=wg->next) {
01510     count++;
01511     if (wg->mark == TRUE) continue;
01512     for(we=wg->next;we;we=we->next) {
01513       if (we->mark == TRUE) continue;
01514       /* find the word with exactly the same time and score */
01515       if (wg->wid == we->wid &&
01516           wg->headphone == we->headphone &&
01517           wg->tailphone == we->tailphone &&
01518           wg->lefttime == we->lefttime &&
01519           wg->righttime == we->righttime &&
01520           wg->fscore_head == we->fscore_head &&
01521           wg->fscore_tail == we->fscore_tail) {
01522         /* merge contexts */
01523         merge_contexts(wg, we);
01524         /* swap contexts of left / right contexts */
01525         for(i=0;i<we->leftwordnum;i++) {
01526           if (we->leftword[i]->mark) continue;
01527           //if (we->leftword[i] == wg) continue;
01528           swap_rightword(we->leftword[i], we, wg, we->left_lscore[i]);
01529         }
01530         for(i=0;i<we->rightwordnum;i++) {
01531           if (we->rightword[i]->mark) continue;
01532           //if (we->rightword[i] == wg) continue;
01533           swap_leftword(we->rightword[i], we, wg, we->right_lscore[i]);
01534         }
01535         we->mark = TRUE;
01536         merged++;
01537       }
01538     }
01539   }
01540 
01541   erased = wordgraph_exec_erase(rootp);
01542 
01543   for(wg=*rootp;wg;wg=wg->next) {
01544     uniq_leftword(wg);
01545     uniq_rightword(wg);
01546   }
01547 
01548   *rest_ret = count - erased;
01549   *merged_ret = merged;
01550 }
01551 
01594 void
01595 wordgraph_adjust_boundary(WordGraph **rootp, RecogProcess *r)
01596 {
01597 #ifdef GRAPHOUT_PRECISE_BOUNDARY
01598   WordGraph *wg;
01599   int mov_num, dup_num, del_num, mod_num;
01600   int count, merged;
01601   boolean flag;
01602   int loopcount;
01603   int maxfnum;
01604   int *framelist;               
01605   LOGPROB *framescorelist;      
01606 
01607   loopcount = 0;
01608 
01609   if (verbose_flag) jlog("STAT: graphout: step 2: adjust boundaries\n");
01610   mov_num = dup_num = del_num = 0;
01611 
01612   /* count number of all words */
01613   count = 0;
01614   for(wg=*rootp;wg;wg=wg->next) count++;
01615   maxfnum = 0;
01616 
01617   do {
01618     /* do adjust */
01619     flag = wordgraph_adjust_boundary_sub(rootp, &mov_num, &dup_num, &del_num, &mod_num, count, &maxfnum, r->peseqlen, r->lmtype, &framelist, &framescorelist);
01620     /* do compaction */
01621     wordgraph_compaction_thesame_sub(rootp, &count, &merged);
01622     if (verbose_flag) jlog("STAT: graphout: #%d: %d moved, %d duplicated, %d purged, %d modified, %d idential, %d left\n", loopcount + 1, mov_num, dup_num, del_num, mod_num, merged, count);
01623 #ifdef GRAPHOUT_LIMIT_BOUNDARY_LOOP
01624     if (++loopcount >= r->config->graph.graphout_limit_boundary_loop_num) {
01625       if (verbose_flag) jlog("STAT: graphout: loop count reached %d, terminate loop now\n", r->config->graph.graphout_limit_boundary_loop_num);
01626       break;
01627     }
01628 #endif
01629   } while (flag);
01630 
01631   /* free work area allocated in adjust_boundary_sub */
01632   if (maxfnum > 0) {
01633     free(framescorelist);
01634     free(framelist);
01635   }
01636 
01637   /* execute erase of marked words */
01638   wordgraph_exec_erase(rootp);
01639 
01640 #else
01641 
01642   if (verbose_flag) jlog("STAT: graphout: step 2: SKIP (adjusting boundaries)\n");
01643 
01644 #endif /* GRAPHOUT_PRECISE_BOUNDARY */
01645 
01646 }
01647 
01648  
01670 void
01671 wordgraph_compaction_thesame(WordGraph **rootp)
01672 {
01673   int rest, erased;
01674 
01675   if (verbose_flag) jlog("STAT: graphout: step 3: merge idential hypotheses (same score, boundary, context)\n");
01676   wordgraph_compaction_thesame_sub(rootp, &rest, &erased);
01677   if (verbose_flag) jlog("STAT: graphout: %d words merged, %d words left in lattice\n", erased, rest);
01678 }
01679 
01708 void
01709 wordgraph_compaction_exacttime(WordGraph **rootp, RecogProcess *r)
01710 {
01711   WordGraph *wg, *we;
01712   int i, count, erased;
01713 
01714   if (r->config->graph.graph_merge_neighbor_range < 0) {
01715     if (verbose_flag) jlog("STAT: graphout: step 4: SKIP (merge the same words with same boundary to the most likely one\n");
01716     return;
01717   }
01718 
01719   if (verbose_flag) jlog("STAT: graphout: step 4: merge same words with same boundary to the most likely one\n");
01720 
01721   count = 0;
01722   for(wg=*rootp;wg;wg=wg->next) {
01723     count++;
01724     if (wg->mark == TRUE) continue;
01725     for(we=wg->next;we;we=we->next) {
01726       if (we->mark == TRUE) continue;
01727       /* find same words at same position */
01728       if (wg->wid == we->wid &&
01729           wg->lefttime == we->lefttime &&
01730           wg->righttime == we->righttime) {
01731         /* merge contexts */
01732         merge_contexts(wg, we);
01733         /* swap contexts of left / right contexts */
01734         for(i=0;i<we->leftwordnum;i++) {
01735           swap_rightword(we->leftword[i], we, wg, we->left_lscore[i]);
01736         }
01737         for(i=0;i<we->rightwordnum;i++) {
01738           swap_leftword(we->rightword[i], we, wg, we->right_lscore[i]);
01739         }
01740         /* keep the max score */
01741         if (wg->fscore_head < we->fscore_head) {
01742           wg->headphone = we->headphone;
01743           wg->tailphone = we->tailphone;
01744           wg->fscore_head = we->fscore_head;
01745           wg->fscore_tail = we->fscore_tail;
01746           wg->gscore_head = we->gscore_head;
01747           wg->gscore_tail = we->gscore_tail;
01748           wg->lscore_tmp = we->lscore_tmp;
01749 #ifdef CM_SEARCH
01750           wg->cmscore = we->cmscore;
01751 #endif
01752           wg->amavg = we->amavg;
01753         }
01754         we->mark = TRUE;
01755       }
01756     }
01757   }
01758   erased = wordgraph_exec_erase(rootp);
01759   if (verbose_flag) jlog("STAT: graphout: %d words merged, %d words left in lattice\n", erased, count-erased);
01760 
01761   for(wg=*rootp;wg;wg=wg->next) {
01762     uniq_leftword(wg);
01763     uniq_rightword(wg);
01764   }
01765 }
01766 
01794 void
01795 wordgraph_compaction_neighbor(WordGraph **rootp, RecogProcess *r)
01796 {
01797   WordGraph *wg, *we;
01798   int i, count, erased;
01799 
01800   if (r->config->graph.graph_merge_neighbor_range <= 0) {
01801     if (verbose_flag) jlog("STAT: graphout: step 5: SKIP (merge the same words around)\n");
01802     return;
01803   }
01804 
01805   if (verbose_flag) jlog("STAT: graphout: step 5: merge same words around, with %d frame margin\n", r->config->graph.graph_merge_neighbor_range);
01806 
01807   count = 0;
01808   for(wg=*rootp;wg;wg=wg->next) {
01809     count++;
01810     if (wg->mark == TRUE) continue;
01811     for(we=wg->next;we;we=we->next) {
01812       if (we->mark == TRUE) continue;
01813       if (wg->wid == we->wid &&
01814           abs(wg->lefttime - we->lefttime) <= r->config->graph.graph_merge_neighbor_range &&
01815           abs(wg->righttime - we->righttime) <= r->config->graph.graph_merge_neighbor_range) {
01816         /* merge contexts */
01817         merge_contexts(wg, we);
01818         /* swap contexts of left / right contexts */
01819         for(i=0;i<we->leftwordnum;i++) {
01820           swap_rightword(we->leftword[i], we, wg, we->left_lscore[i]);
01821         }
01822         for(i=0;i<we->rightwordnum;i++) {
01823           swap_leftword(we->rightword[i], we, wg, we->right_lscore[i]);
01824         }
01825         /* keep the max score */
01826         if (wg->fscore_head < we->fscore_head) {
01827           wg->headphone = we->headphone;
01828           wg->tailphone = we->tailphone;
01829           wg->fscore_head = we->fscore_head;
01830           wg->fscore_tail = we->fscore_tail;
01831           wg->gscore_head = we->gscore_head;
01832           wg->gscore_tail = we->gscore_tail;
01833           wg->lscore_tmp = we->lscore_tmp;
01834 #ifdef CM_SEARCH
01835           wg->cmscore = we->cmscore;
01836 #endif
01837           wg->amavg = we->amavg;
01838         }
01839         we->mark = TRUE;
01840       }
01841     }
01842   }
01843   erased = wordgraph_exec_erase(rootp);
01844   if (verbose_flag) jlog("STAT: graphout: %d words merged, %d words left in lattice\n", erased, count-erased);
01845 
01846   for(wg=*rootp;wg;wg=wg->next) {
01847     uniq_leftword(wg);
01848     uniq_rightword(wg);
01849   }
01850  
01851 }
01852 
01853 /**************************************************************/
01854 /* generation of graph word candidates while search */
01855 
01900 WordGraph *
01901 wordgraph_assign(WORD_ID wid, WORD_ID wid_left, WORD_ID wid_right, int leftframe, int rightframe, LOGPROB fscore_head, LOGPROB fscore_tail, LOGPROB gscore_head, LOGPROB gscore_tail, LOGPROB lscore, LOGPROB cm, RecogProcess *r)
01902 {
01903   WordGraph *newarc;
01904   HMM_Logical *l, *ret, *head, *tail;
01905   WORD_INFO *winfo;
01906 
01907   winfo = r->lm->winfo;
01908 
01909   /* find context dependent phones at head and tail */
01910   l = winfo->wseq[wid][winfo->wlen[wid]-1];
01911   if (wid_right != WORD_INVALID) {
01912     ret = get_right_context_HMM(l, winfo->wseq[wid_right][0]->name, r->am->hmminfo);
01913     if (ret != NULL) l = ret;
01914   }
01915   if (winfo->wlen[wid] > 1) {
01916     tail = l;
01917     l = winfo->wseq[wid][0];
01918   }
01919   if (wid_left != WORD_INVALID) {
01920     ret = get_left_context_HMM(l, winfo->wseq[wid_left][winfo->wlen[wid_left]-1]->name, r->am->hmminfo);
01921     if (ret != NULL) l = ret;
01922   }
01923   head = l;
01924   if (winfo->wlen[wid] <= 1) {
01925     tail = l;
01926   }
01927 
01928   /* generate a new graph word hypothesis */
01929   newarc = wordgraph_new(wid, head, tail, leftframe, rightframe, fscore_head, fscore_tail, gscore_head, gscore_tail, lscore, cm);
01930   //jlog("DEBUG:     [%d..%d] %d\n", leftframe, rightframe, wid);
01931   return newarc;
01932 }
01933 
01956 void
01957 wordgraph_save(WordGraph *wg, WordGraph *right, WordGraph **root)
01958 {
01959   if (wg != NULL) {
01960     wg->next = *root;
01961     *root = wg;
01962     wg->saved = TRUE;
01963     wordgraph_add_leftword(right, wg, wg->lscore_tmp);
01964     wordgraph_add_rightword(wg, right, wg->lscore_tmp);
01965   }
01966 }
01967 
01968 #ifdef GRAPHOUT_DYNAMIC
01969 
02019 WordGraph *
02020 wordgraph_check_merge(WordGraph *now, WordGraph **root, WORD_ID next_wid, boolean *merged_p, JCONF_SEARCH *jconf)
02021 {
02022   WordGraph *wg;
02023   int i;
02024 #ifdef GDEBUG
02025   WordGraph *w;
02026 #endif
02027 
02028 #ifdef GRAPHOUT_SEARCH
02029   *merged_p = FALSE;
02030 #endif
02031 
02032   if (now == NULL) return(NULL);
02033 
02034 #ifdef GDEBUG
02035   jlog("DEBUG: check_merge: checking \"%s\"[%d..%d]\n", wchmm_local->winfo->woutput[now->wid], now->lefttime, now->righttime);
02036   for(i=0;i<now->leftwordnum;i++) {
02037     w = now->leftword[i];
02038     jlog("DEBUG: \t left%d:  \"%15s\"[%d..%d]\n", i, wchmm_local->winfo->woutput[w->wid], w->lefttime, w->righttime);
02039   }
02040   for(i=0;i<now->rightwordnum;i++) {
02041     w = now->rightword[i];
02042     jlog("DEBUG: \tright%d:  \"%15s\"[%d..%d]\n", i, wchmm_local->winfo->woutput[w->wid], w->lefttime, w->righttime);
02043   }
02044 #endif
02045 
02046   for(wg=*root;wg;wg=wg->next) {
02047     if (wg == now) continue;
02048 #ifdef GRAPHOUT_DYNAMIC
02049     /* skip already merged word */
02050     if (wg->purged) continue;
02051 #endif
02052     if (jconf->graph.graph_merge_neighbor_range < 0) {
02053       /* when no merging, words with different triphone context at word edge
02054          should be differenciated */
02055       if (wg->headphone != now->headphone || wg->tailphone != now->tailphone) {
02056         continue;
02057       }
02058     }
02059     if (wg->wid == now->wid
02060         && wg->lefttime == now->lefttime
02061         && wg->righttime == now->righttime) {
02062       /* same word on the same position is found in current word graph */
02063 #ifdef GDEBUG
02064       jlog("DEBUG: check_merge: same word found: \"%s\"[%d..%d]\n", wchmm_local->winfo->woutput[wg->wid], wg->lefttime, wg->righttime);
02065       for(i=0;i<wg->leftwordnum;i++) {
02066         w = wg->leftword[i];
02067         jlog("DEBUG: \t left%d:  \"%15s\"[%d..%d]\n", i, wchmm_local->winfo->woutput[w->wid], w->lefttime, w->righttime);
02068       }
02069       for(i=0;i<wg->rightwordnum;i++) {
02070         w = wg->rightword[i];
02071         jlog("DEBUG: \tright%d:  \"%15s\"[%d..%d]\n", i, wchmm_local->winfo->woutput[w->wid], w->lefttime, w->righttime);
02072       }
02073 #endif
02074       /* merge contexts */
02075       merge_contexts(wg, now);
02076       /* swap contexts of left / right contexts */
02077       for(i=0;i<now->leftwordnum;i++) {
02078         swap_rightword(now->leftword[i], now, wg, now->left_lscore[i]);
02079         uniq_rightword(now->leftword[i]);
02080       }
02081       for(i=0;i<now->rightwordnum;i++) {
02082         swap_leftword(now->rightword[i], now, wg, now->right_lscore[i]);
02083         uniq_leftword(now->rightword[i]);
02084       }
02085 #ifdef GRAPHOUT_SEARCH
02086       /* if the left and right contexts of now are already included in wg,
02087          and wg already has left node of next word,
02088          it means that
02089          the current word and the last word context is
02090          already included in the existing word graph.
02091          So, in the case this partial path should be abandoned.
02092       */
02093       for(i=0;i<wg->leftwordnum;i++) {
02094         if (wg->leftword[i]->wid == next_wid) break;
02095       }
02096       if (i < wg->leftwordnum) {
02097         *merged_p = TRUE;
02098       }
02099 #endif /* GRAPHOUT_SEARCH */
02100 #ifdef GRAPHOUT_OVERWRITE
02101       /*  if current hypothesis score is higher than saved,
02102           overwrite the scores and not terminate */
02103       if (
02104 #ifdef GRAPHOUT_OVERWRITE_GSCORE
02105           //wg->gscore_head < now->gscore_head
02106           wg->amavg < now->amavg;
02107 #else
02108           wg->fscore_head < now->fscore_head
02109 #endif
02110           ) {
02111           wg->headphone = now->headphone;
02112           wg->tailphone = now->tailphone;
02113           wg->fscore_head = now->fscore_head;
02114           wg->fscore_tail = now->fscore_tail;
02115           wg->gscore_head = now->gscore_head;
02116           wg->gscore_tail = now->gscore_tail;
02117           wg->lscore_tmp = now->lscore_tmp;
02118 #ifdef CM_SEARCH
02119           wg->cmscore = now->cmscore;
02120 #endif
02121           wg->amavg = now->amavg;
02122 #ifdef GRAPHOUT_SEARCH
02123           *merged_p = FALSE;
02124 #endif
02125       }
02126 #endif /* GRAPHOUT_OVERWRITE */
02127       /* the merged word should be discarded for later merging from
02128          another word, so disable this */
02129       now->purged = TRUE;
02130       
02131       /* return the found one */
02132       return wg;
02133     }
02134   }
02135   /* if the same word not found, return NULL */
02136   return NULL;
02137 }
02138 #endif /* GRAPHOUT_DYNAMIC */
02139 
02140 /**************************************************************/
02141 /* misc. functions */
02142 
02192 void
02193 put_wordgraph(FILE *fp, WordGraph *wg, WORD_INFO *winfo)
02194 {
02195   int i;
02196   if (fp == NULL) return;
02197   if (wg == NULL) {
02198     fprintf(fp, "(NULL)\n");
02199   } else {
02200     fprintf(fp, "%d:", wg->id);
02201     fprintf(fp, " [%d..%d]", wg->lefttime, wg->righttime);
02202     for(i=0;i<wg->leftwordnum;i++) {
02203       fprintf(fp, (i == 0) ? " left=%d" : ",%d", wg->leftword[i]->id);
02204     }
02205     for(i=0;i<wg->rightwordnum;i++) {
02206       fprintf(fp, (i == 0) ? " right=%d" : ",%d", wg->rightword[i]->id);
02207     }
02208     for(i=0;i<wg->leftwordnum;i++) {
02209       fprintf(fp, (i == 0) ? " left_lscore=%f" : ",%f", wg->left_lscore[i]);
02210     }
02211     for(i=0;i<wg->rightwordnum;i++) {
02212       fprintf(fp, (i == 0) ? " right_lscore=%f" : ",%f", wg->right_lscore[i]);
02213     }
02214     fprintf(fp, " lscore_tmp=%f", wg->lscore_tmp);
02215 
02216     fprintf(fp, " wid=%d name=\"%s\" lname=\"%s\" f=%f f_prev=%f g_head=%f g_prev=%f", wg->wid, winfo->woutput[wg->wid], winfo->wname[wg->wid], wg->fscore_head, wg->fscore_tail, wg->gscore_head, wg->gscore_tail);
02217     fprintf(fp, " forward_score=%f backword_score=%f", wg->forward_score, wg->backward_score);
02218     if (wg->righttime - wg->lefttime + 1 != 0) {
02219       fprintf(fp, " AMavg=%f", wg->amavg);
02220     }
02221 #ifdef CM_SEARCH
02222     fprintf(fp, " cmscore=%f", wg->cmscore);
02223 #endif
02224     fprintf(fp, " graphcm=%f", wg->graph_cm);
02225     fprintf(fp, " headphone=%s", wg->headphone->name);
02226     fprintf(fp, " tailphone=%s", wg->tailphone->name);
02227     fprintf(fp, "\n");
02228   }
02229 }
02230 
02251 void
02252 wordgraph_dump(FILE *fp, WordGraph *root, WORD_INFO *winfo)
02253 {
02254   WordGraph *wg;
02255   fprintf(fp, "--- begin wordgraph data ---\n");
02256   for(wg=root;wg;wg=wg->next) {
02257     put_wordgraph(fp, wg, winfo);
02258   }
02259   fprintf(fp, "--- end wordgraph data ---\n");
02260 }
02261 
02280 void
02281 wordgraph_check_coherence(WordGraph *rootp, RecogProcess *r)
02282 {
02283   WordGraph *wg, *wl, *wr;
02284   int nl, nr;
02285   WORD_INFO *winfo;
02286 
02287   winfo = r->lm->winfo;
02288 
02289   for(wg=rootp;wg;wg=wg->next) {
02290     /* check ID overflow */
02291     if (wg->id < 0 || wg->id >= r->graph_totalwordnum) {
02292       jlog("ERROR: invalid graph word id \"%d\" (should be [0..%d])\n", wg->id, r->graph_totalwordnum-1);
02293       put_wordgraph(jlog_get_fp(), wg, winfo);
02294       continue;
02295     }
02296     /* check link */
02297     for(nl=0;nl<wg->leftwordnum;nl++){
02298       wl = wg->leftword[nl];
02299       if (wl->id < 0 || wl->id >= r->graph_totalwordnum) {
02300         jlog("ERROR: invalid graph word id \"%d\" (should be [0..%d]) in left context\n", wl->id, r->graph_totalwordnum-1);
02301         put_wordgraph(jlog_get_fp(), wg, winfo);
02302         continue;
02303       }
02304       for(nr=0;nr<wl->rightwordnum;nr++){
02305         if (wl->rightword[nr] == wg) break;
02306       }
02307       if (nr >= wl->rightwordnum) {
02308         jlog("ERROR: on graph, reverse link not found in left context\n");
02309         put_wordgraph(jlog_get_fp(), wg, winfo);
02310         put_wordgraph(jlog_get_fp(), wl, winfo);
02311         continue;
02312       }
02313     }
02314     for(nr=0;nr<wg->rightwordnum;nr++){
02315       wr = wg->rightword[nr];
02316       if (wr->id < 0 || wr->id >= r->graph_totalwordnum) {
02317         jlog("ERROR: invalid graph word id \"%d\" (should be [0..%d]) in right context\n", wr->id, r->graph_totalwordnum-1);
02318         put_wordgraph(jlog_get_fp(), wg, winfo);
02319         continue;
02320       }
02321       for(nl=0;nl<wr->leftwordnum;nl++){
02322         if (wr->leftword[nl] == wg) break;
02323       }
02324       if (nl >= wr->leftwordnum) {
02325         jlog("ERROR: on graph, reverse link not found in left context\n");
02326         put_wordgraph(jlog_get_fp(), wg, winfo);
02327         put_wordgraph(jlog_get_fp(), wr, winfo);
02328         continue;
02329       }
02330     }
02331   }
02332 }
02333 
02334 
02335 /* lattice-based posterior probability computation by forward-backward
02336    algorithm */
02351 static int
02352 compare_forward(WordGraph **x, WordGraph **y)
02353 {
02354   if ((*x)->righttime < (*y)->righttime) return 1;
02355   else if ((*x)->righttime > (*y)->righttime) return -1;
02356   else return 0;
02357 }
02358 
02373 static int
02374 compare_backward(WordGraph **x, WordGraph **y)
02375 {
02376   if ((*x)->lefttime < (*y)->lefttime) return -1;
02377   else if ((*x)->lefttime > (*y)->lefttime) return 1;
02378   else return 0;
02379 }
02380 
02395 static LOGPROB 
02396 addlog10(LOGPROB x, LOGPROB y)
02397 {
02398   if (x < y) {
02399     //return(y + log10(1 + pow(10, x-y)));
02400     return(y + log(1 + pow(10, x-y)) * INV_LOG_TEN);
02401   } else {
02402     return(x + log(1 + pow(10, y-x)) * INV_LOG_TEN);
02403   }
02404 }
02405 
02429 void
02430 graph_forward_backward(WordGraph *root, RecogProcess *r)
02431 {
02432   WordGraph *wg, *left, *right;
02433   int i, j;
02434   LOGPROB s;
02435   LOGPROB sum1, sum2;
02436   int count;
02437   WordGraph **wlist;
02438   LOGPROB cm_alpha;
02439 
02440   cm_alpha = r->config->annotate.cm_alpha;
02441 
02442   /* make a wordgraph list for frame-sorted access */
02443   count = 0;
02444   for(wg=root;wg;wg=wg->next) count++;
02445   if (count == 0) return;
02446   wlist = (WordGraph **)mymalloc(sizeof(WordGraph *) * count);
02447   i = 0;
02448   for(wg=root;wg;wg=wg->next) {
02449     wlist[i++] = wg;
02450   }
02451 
02452   /* sort wordgraph list downward by the right frame*/
02453   qsort(wlist, count, sizeof(WordGraph *), (int (*)(const void *, const void *))compare_forward);
02454   /* clear forward scores */
02455   for(wg=root;wg;wg=wg->next) {
02456     wg->forward_score = LOG_ZERO;
02457   }
02458   /* forward procedure */
02459   sum1 = LOG_ZERO;
02460   for(i=0;i<count;i++) {
02461     wg = wlist[i];
02462     if (wg->righttime == r->peseqlen - 1) {
02463       /* set initial score */
02464       wg->forward_score = 0.0;
02465       //wg->forward_score += wg->lscore * cm_alpha;
02466     } else {
02467       /* (just a bogus check...) */
02468       if (wg->forward_score == LOG_ZERO) {
02469         wordgraph_dump(stdout, root, r->lm->winfo);
02470         put_wordgraph(stdout, wg, r->lm->winfo);
02471         j_internal_error("NO CONTEXT?\n");
02472       }
02473     }
02474     /* propagate scores */
02475     s = wg->amavg * (wg->righttime - wg->lefttime + 1);
02476     s *= cm_alpha;
02477     s += wg->forward_score;
02478     if (wg->lefttime == 0) {
02479       /* add for sum */
02480       sum1 = addlog10(sum1, s);
02481     } else {
02482       /* propagate to left words */
02483       for(j=0;j<wg->leftwordnum;j++) {
02484         left = wg->leftword[j];
02485         left->forward_score = addlog10(left->forward_score, s + wg->left_lscore[j] * cm_alpha);
02486       }
02487     }
02488   }
02489 
02490   /* sort wordgraph list downward by the right score */
02491   qsort(wlist, count, sizeof(WordGraph *), (int (*)(const void *, const void *))compare_backward);
02492   /* clear backward scores */
02493   for(wg=root;wg;wg=wg->next) {
02494     wg->backward_score = LOG_ZERO;
02495   }
02496   /* backward procedure */
02497   sum2 = LOG_ZERO;
02498   for(i=0;i<count;i++) {
02499     wg = wlist[i];
02500     if (wg->lefttime == 0) {
02501       /* set initial score */
02502       wg->backward_score = 0.0;
02503     } else {
02504       /* (just a bogus check...) */
02505       if (wg->backward_score == LOG_ZERO) {
02506         put_wordgraph(stdout, wg, r->lm->winfo);
02507         j_internal_error("NO CONTEXT?\n");
02508       }
02509     }
02510     /* propagate scores */
02511     s = wg->amavg * (wg->righttime - wg->lefttime + 1);
02512     s *= cm_alpha;
02513     s += wg->backward_score;
02514     if (wg->righttime == r->peseqlen - 1) {
02515       /* add for sum */
02516       //sum2 = addlog10(sum2, s + wg->lscore * cm_alpha);
02517       sum2 = addlog10(sum2, s);
02518     } else {
02519       for(j=0;j<wg->rightwordnum;j++) {
02520         right = wg->rightword[j];
02521         right->backward_score = addlog10(right->backward_score, s + wg->right_lscore[j] * cm_alpha);
02522       }
02523     }
02524   }
02525 
02526   if (verbose_flag) jlog("STAT: graph_cm: forward score = %f, backward score = %f\n", sum1, sum2);
02527 
02528   /* compute CM */
02529   for(wg=root;wg;wg=wg->next) {
02530     s = wg->amavg * (wg->righttime - wg->lefttime + 1);
02531     s *= cm_alpha;
02532     s = wg->backward_score + s + wg->forward_score;
02533     wg->graph_cm = pow(10, s - sum1);
02534     //wg->graph_cm = s - sum1;
02535   }
02536 
02537   free(wlist);
02538 
02539 }
02540 /* end of file */