src/libpocketsphinx/ngram_search.c

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
00002 /* ====================================================================
00003  * Copyright (c) 2008 Carnegie Mellon University.  All rights
00004  * reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer. 
00012  *
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in
00015  *    the documentation and/or other materials provided with the
00016  *    distribution.
00017  *
00018  * This work was supported in part by funding from the Defense Advanced 
00019  * Research Projects Agency and the National Science Foundation of the 
00020  * United States of America, and the CMU Sphinx Speech Consortium.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 
00023  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
00024  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00025  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
00026  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00027  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
00028  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
00029  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
00030  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
00031  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
00032  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  * ====================================================================
00035  *
00036  */
00037 
00042 /* System headers. */
00043 #include <string.h>
00044 #include <assert.h>
00045 
00046 /* SphinxBase headers. */
00047 #include <ckd_alloc.h>
00048 #include <listelem_alloc.h>
00049 #include <err.h>
00050 
00051 /* Local headers. */
00052 #include "pocketsphinx_internal.h"
00053 #include "ps_lattice_internal.h"
00054 #include "ngram_search.h"
00055 #include "ngram_search_fwdtree.h"
00056 #include "ngram_search_fwdflat.h"
00057 
00058 static int ngram_search_start(ps_search_t *search);
00059 static int ngram_search_step(ps_search_t *search, int frame_idx);
00060 static int ngram_search_finish(ps_search_t *search);
00061 static int ngram_search_reinit(ps_search_t *search);
00062 static char const *ngram_search_hyp(ps_search_t *search, int32 *out_score);
00063 static int32 ngram_search_prob(ps_search_t *search);
00064 static ps_seg_t *ngram_search_seg_iter(ps_search_t *search, int32 *out_score);
00065 
00066 static ps_searchfuncs_t ngram_funcs = {
00067     /* name: */   "ngram",
00068     /* start: */  ngram_search_start,
00069     /* step: */   ngram_search_step,
00070     /* finish: */ ngram_search_finish,
00071     /* reinit: */ ngram_search_reinit,
00072     /* free: */   ngram_search_free,
00073     /* lattice: */  ngram_search_lattice,
00074     /* hyp: */      ngram_search_hyp,
00075     /* prob: */     ngram_search_prob,
00076     /* seg_iter: */ ngram_search_seg_iter,
00077 };
00078 
00079 static void
00080 ngram_search_update_widmap(ngram_search_t *ngs)
00081 {
00082     const char **words;
00083     int32 i, n_words;
00084 
00085     /* It's okay to include fillers since they won't be in the LM */
00086     n_words = ps_search_n_words(ngs);
00087     words = ckd_calloc(n_words, sizeof(*words));
00088     /* This will include alternates, again, that's okay since they aren't in the LM */
00089     for (i = 0; i < n_words; ++i)
00090         words[i] = (const char *)s3dict_wordstr(ps_search_dict(ngs), i);
00091     ngram_model_set_map_words(ngs->lmset, words, n_words);
00092     ckd_free(words);
00093 }
00094 
00095 static void
00096 ngram_search_calc_beams(ngram_search_t *ngs)
00097 {
00098     cmd_ln_t *config;
00099     acmod_t *acmod;
00100 
00101     config = ps_search_config(ngs);
00102     acmod = ps_search_acmod(ngs);
00103 
00104     /* Log beam widths. */
00105     ngs->beam = logmath_log(acmod->lmath, cmd_ln_float64_r(config, "-beam"));
00106     ngs->wbeam = logmath_log(acmod->lmath, cmd_ln_float64_r(config, "-wbeam"));
00107     ngs->pbeam = logmath_log(acmod->lmath, cmd_ln_float64_r(config, "-pbeam"));
00108     ngs->lpbeam = logmath_log(acmod->lmath, cmd_ln_float64_r(config, "-lpbeam"));
00109     ngs->lponlybeam = logmath_log(acmod->lmath, cmd_ln_float64_r(config, "-lponlybeam"));
00110     ngs->fwdflatbeam = logmath_log(acmod->lmath, cmd_ln_float64_r(config, "-fwdflatbeam"));
00111     ngs->fwdflatwbeam = logmath_log(acmod->lmath, cmd_ln_float64_r(config, "-fwdflatwbeam"));
00112 
00113     /* Absolute pruning parameters. */
00114     ngs->maxwpf = cmd_ln_int32_r(config, "-maxwpf");
00115     ngs->maxhmmpf = cmd_ln_int32_r(config, "-maxhmmpf");
00116 
00117     /* Various penalties which may or may not be useful. */
00118     ngs->wip = logmath_log(acmod->lmath, cmd_ln_float32_r(config, "-wip"));
00119     ngs->nwpen = logmath_log(acmod->lmath, cmd_ln_float32_r(config, "-nwpen"));
00120     ngs->pip = logmath_log(acmod->lmath, cmd_ln_float32_r(config, "-pip"));
00121     ngs->silpen = ngs->pip
00122         + logmath_log(acmod->lmath, cmd_ln_float32_r(config, "-silprob"));
00123     ngs->fillpen = ngs->pip
00124         + logmath_log(acmod->lmath, cmd_ln_float32_r(config, "-fillprob"));
00125 
00126     /* Language weight ratios for fwdflat and bestpath search. */
00127     ngs->fwdflat_fwdtree_lw_ratio =
00128         cmd_ln_float32_r(config, "-fwdflatlw")
00129         / cmd_ln_float32_r(config, "-lw");
00130     ngs->bestpath_fwdtree_lw_ratio =
00131         cmd_ln_float32_r(config, "-bestpathlw")
00132         / cmd_ln_float32_r(config, "-lw");
00133 
00134     /* Acoustic score scale for posterior probabilities. */
00135     ngs->ascale = 1.0 / cmd_ln_float32_r(config, "-ascale");
00136 }
00137 
00138 ps_search_t *
00139 ngram_search_init(cmd_ln_t *config,
00140                   acmod_t *acmod,
00141                   s3dict_t *dict,
00142                   dict2pid_t *d2p)
00143 {
00144     ngram_search_t *ngs;
00145     const char *path;
00146 
00147     ngs = ckd_calloc(1, sizeof(*ngs));
00148     ps_search_init(&ngs->base, &ngram_funcs, config, acmod, dict, d2p);
00149     ngs->hmmctx = hmm_context_init(bin_mdef_n_emit_state(acmod->mdef),
00150                                    acmod->tmat->tp, NULL, acmod->mdef->sseq);
00151     if (ngs->hmmctx == NULL) {
00152         ps_search_free(ps_search_base(ngs));
00153         return NULL;
00154     }
00155     ngs->chan_alloc = listelem_alloc_init(sizeof(chan_t));
00156     ngs->root_chan_alloc = listelem_alloc_init(sizeof(root_chan_t));
00157     ngs->latnode_alloc = listelem_alloc_init(sizeof(ps_latnode_t));
00158 
00159     /* Calculate various beam widths and such. */
00160     ngram_search_calc_beams(ngs);
00161 
00162     /* Allocate a billion different tables for stuff. */
00163     ngs->word_chan = ckd_calloc(s3dict_size(dict),
00164                                 sizeof(*ngs->word_chan));
00165     ngs->word_lat_idx = ckd_calloc(s3dict_size(dict),
00166                                    sizeof(*ngs->word_lat_idx));
00167     ngs->zeroPermTab = ckd_calloc(bin_mdef_n_ciphone(acmod->mdef),
00168                                   sizeof(*ngs->zeroPermTab));
00169     ngs->word_active = bitvec_alloc(s3dict_size(dict));
00170     ngs->last_ltrans = ckd_calloc(s3dict_size(dict),
00171                                   sizeof(*ngs->last_ltrans));
00172 
00173     /* FIXME: All these structures need to be made dynamic with
00174      * garbage collection. */
00175     ngs->bp_table_size = cmd_ln_int32_r(config, "-latsize");
00176     ngs->bp_table = ckd_calloc(ngs->bp_table_size,
00177                                sizeof(*ngs->bp_table));
00178     /* FIXME: This thing is frickin' huge. */
00179     ngs->bscore_stack_size = ngs->bp_table_size * 20;
00180     ngs->bscore_stack = ckd_calloc(ngs->bscore_stack_size,
00181                                    sizeof(*ngs->bscore_stack));
00182     ngs->n_frame_alloc = 256;
00183     ngs->bp_table_idx = ckd_calloc(ngs->n_frame_alloc + 1,
00184                                    sizeof(*ngs->bp_table_idx));
00185     ++ngs->bp_table_idx; /* Make bptableidx[-1] valid */
00186 
00187     /* Allocate active word list array */
00188     ngs->active_word_list = ckd_calloc_2d(2, s3dict_size(dict),
00189                                           sizeof(**ngs->active_word_list));
00190 
00191     /* Load language model(s) */
00192     if ((path = cmd_ln_str_r(config, "-lmctl"))) {
00193         ngs->lmset = ngram_model_set_read(config, path, acmod->lmath);
00194         if (ngs->lmset == NULL) {
00195             E_ERROR("Failed to read language model control file: %s\n",
00196                     path);
00197             goto error_out;
00198         }
00199         /* Set the default language model if needed. */
00200         if ((path = cmd_ln_str_r(config, "-lmname"))) {
00201             ngram_model_set_select(ngs->lmset, path);
00202         }
00203     }
00204     else if ((path = cmd_ln_str_r(config, "-lm"))) {
00205         static const char *name = "default";
00206         ngram_model_t *lm;
00207 
00208         lm = ngram_model_read(config, path, NGRAM_AUTO, acmod->lmath);
00209         if (lm == NULL) {
00210             E_ERROR("Failed to read language model file: %s\n", path);
00211             goto error_out;
00212         }
00213         ngs->lmset = ngram_model_set_init(config,
00214                                           &lm, (char **)&name,
00215                                           NULL, 1);
00216         if (ngs->lmset == NULL) {
00217             E_ERROR("Failed to initialize language model set\n");
00218             goto error_out;
00219         }
00220     }
00221 
00222     /* Create word mappings. */
00223     ngram_search_update_widmap(ngs);
00224 
00225     /* Initialize fwdtree, fwdflat, bestpath modules if necessary. */
00226     if (cmd_ln_boolean_r(config, "-fwdtree")) {
00227         ngram_fwdtree_init(ngs);
00228         ngs->fwdtree = TRUE;
00229     }
00230     if (cmd_ln_boolean_r(config, "-fwdflat")) {
00231         ngram_fwdflat_init(ngs);
00232         ngs->fwdflat = TRUE;
00233     }
00234     if (cmd_ln_boolean_r(config, "-bestpath")) {
00235         ngs->bestpath = TRUE;
00236     }
00237     return (ps_search_t *)ngs;
00238 
00239 error_out:
00240     ngram_search_free((ps_search_t *)ngs);
00241     return NULL;
00242 }
00243 
00244 static int
00245 ngram_search_reinit(ps_search_t *search)
00246 {
00247     ngram_search_t *ngs = (ngram_search_t *)search;
00248     int rv = 0;
00249 
00250     /*
00251      * NOTE!!! This is not a general-purpose reinit function.  It only
00252      * deals with updates to the language model set and beam widths.
00253      */
00254 
00255     /* Update beam widths. */
00256     ngram_search_calc_beams(ngs);
00257 
00258     /* Update word mappings. */
00259     ngram_search_update_widmap(ngs);
00260 
00261     /* Now rebuild lextrees or what have you. */
00262     if (ngs->fwdtree) {
00263         if ((rv = ngram_fwdtree_reinit(ngs)) < 0)
00264             return rv;
00265     }
00266     if (ngs->fwdflat) {
00267         if ((rv = ngram_fwdflat_reinit(ngs)) < 0)
00268             return rv;
00269     }
00270 
00271     return rv;
00272 }
00273 
00274 void
00275 ngram_search_free(ps_search_t *search)
00276 {
00277     ngram_search_t *ngs = (ngram_search_t *)search;
00278 
00279     ps_search_deinit(search);
00280     if (ngs->fwdtree)
00281         ngram_fwdtree_deinit(ngs);
00282     if (ngs->fwdflat)
00283         ngram_fwdflat_deinit(ngs);
00284 
00285     hmm_context_free(ngs->hmmctx);
00286     listelem_alloc_free(ngs->chan_alloc);
00287     listelem_alloc_free(ngs->root_chan_alloc);
00288     listelem_alloc_free(ngs->latnode_alloc);
00289     ngram_model_free(ngs->lmset);
00290 
00291     ckd_free(ngs->word_chan);
00292     ckd_free(ngs->word_lat_idx);
00293     ckd_free(ngs->zeroPermTab);
00294     bitvec_free(ngs->word_active);
00295     ckd_free(ngs->bp_table);
00296     ckd_free(ngs->bscore_stack);
00297     if (ngs->bp_table_idx != NULL)
00298         ckd_free(ngs->bp_table_idx - 1);
00299     ckd_free_2d(ngs->active_word_list);
00300     ckd_free(ngs->last_ltrans);
00301     ckd_free(ngs);
00302 }
00303 
00304 int
00305 ngram_search_mark_bptable(ngram_search_t *ngs, int frame_idx)
00306 {
00307     if (frame_idx >= ngs->n_frame_alloc) {
00308         ngs->n_frame_alloc *= 2;
00309         ngs->bp_table_idx = ckd_realloc(ngs->bp_table_idx - 1,
00310                                         (ngs->n_frame_alloc + 1)
00311                                         * sizeof(*ngs->bp_table_idx));
00312         if (ngs->frm_wordlist) {
00313             ngs->frm_wordlist = ckd_realloc(ngs->frm_wordlist,
00314                                             ngs->n_frame_alloc
00315                                             * sizeof(*ngs->frm_wordlist));
00316         }
00317         ++ngs->bp_table_idx; /* Make bptableidx[-1] valid */
00318     }
00319     ngs->bp_table_idx[frame_idx] = ngs->bpidx;
00320     return ngs->bpidx;
00321 }
00322 
00326 static void
00327 cache_bptable_paths(ngram_search_t *ngs, int32 bp)
00328 {
00329     int32 w, prev_bp;
00330     bptbl_t *be;
00331 
00332     be = &(ngs->bp_table[bp]);
00333     prev_bp = bp;
00334     w = be->wid;
00335 
00336     while (s3dict_filler_word(ps_search_dict(ngs), w)) {
00337         prev_bp = ngs->bp_table[prev_bp].bp;
00338         w = ngs->bp_table[prev_bp].wid;
00339     }
00340 
00341     be->real_wid = s3dict_basewid(ps_search_dict(ngs), w);
00342 
00343     prev_bp = ngs->bp_table[prev_bp].bp;
00344     be->prev_real_wid =
00345         (prev_bp != NO_BP) ? ngs->bp_table[prev_bp].real_wid : -1;
00346 }
00347 
00348 void
00349 ngram_search_save_bp(ngram_search_t *ngs, int frame_idx,
00350                      int32 w, int32 score, int32 path, int32 rc)
00351 {
00352     int32 _bp_;
00353 
00354     /* Look for an existing exit for this word in this frame. */
00355     _bp_ = ngs->word_lat_idx[w];
00356     if (_bp_ != NO_BP) {
00357         /* Keep only the best scoring one (this is a potential source
00358          * of search errors...) */
00359         if (ngs->bp_table[_bp_].score WORSE_THAN score) {
00360             if (ngs->bp_table[_bp_].bp != path) {
00361                 ngs->bp_table[_bp_].bp = path;
00362                 cache_bptable_paths(ngs, _bp_);
00363             }
00364             ngs->bp_table[_bp_].score = score;
00365         }
00366         /* But do keep track of scores for all right contexts, since
00367          * we need them to determine the starting path scores for any
00368          * successors of this word exit. */
00369         ngs->bscore_stack[ngs->bp_table[_bp_].s_idx + rc] = score;
00370     }
00371     else {
00372         int32 i, rcsize, *bss;
00373         bptbl_t *be;
00374 
00375         /* Expand the backpointer tables if necessary. */
00376         if (ngs->bpidx >= ngs->bp_table_size) {
00377             ngs->bp_table_size *= 2;
00378             ngs->bp_table = ckd_realloc(ngs->bp_table,
00379                                         ngs->bp_table_size
00380                                         * sizeof(*ngs->bp_table));
00381             E_INFO("Resized backpointer table to %d entries\n", ngs->bp_table_size);
00382         }
00383         if (ngs->bss_head >= ngs->bscore_stack_size
00384             - bin_mdef_n_ciphone(ps_search_acmod(ngs)->mdef)) {
00385             ngs->bscore_stack_size *= 2;
00386             ngs->bscore_stack = ckd_realloc(ngs->bscore_stack,
00387                                             ngs->bscore_stack_size
00388                                             * sizeof(*ngs->bscore_stack));
00389             E_INFO("Resized score stack to %d entries\n", ngs->bscore_stack_size);
00390         }
00391 
00392         ngs->word_lat_idx[w] = ngs->bpidx;
00393         be = &(ngs->bp_table[ngs->bpidx]);
00394         be->wid = w;
00395         be->frame = frame_idx;
00396         be->bp = path;
00397         be->score = score;
00398         be->s_idx = ngs->bss_head;
00399         be->valid = TRUE;
00400 
00401         /* DICT2PID */
00402         /* Get diphone ID for final phone and number of ssids corresponding to it. */
00403         be->last_phone = s3dict_last_phone(ps_search_dict(ngs),w);
00404         if (s3dict_pronlen(ps_search_dict(ngs), w) != 1) {
00405             be->last2_phone = s3dict_second_last_phone(ps_search_dict(ngs),w);
00406             rcsize = dict2pid_rssid(ps_search_dict2pid(ngs),
00407                                     be->last_phone, be->last2_phone)->n_ssid;
00408         }
00409         else {
00410             be->last2_phone = -1;
00411             rcsize = 1;
00412         }
00413         /* Allocate some space on the bscore_stack for all of these triphones. */
00414         for (i = rcsize, bss = ngs->bscore_stack + ngs->bss_head; i > 0; --i, bss++)
00415             *bss = WORST_SCORE;
00416         ngs->bscore_stack[ngs->bss_head + rc] = score;
00417         cache_bptable_paths(ngs, ngs->bpidx);
00418 
00419         ngs->bpidx++;
00420         ngs->bss_head += rcsize;
00421     }
00422 }
00423 
00424 int
00425 ngram_search_find_exit(ngram_search_t *ngs, int frame_idx, int32 *out_best_score)
00426 {
00427     /* End of backpointers for this frame. */
00428     int end_bpidx;
00429     int best_exit, bp;
00430     int32 best_score;
00431 
00432     /* No hypothesis means no exit node! */
00433     if (ngs->n_frame == 0)
00434         return NO_BP;
00435 
00436     if (frame_idx == -1 || frame_idx >= ngs->n_frame)
00437         frame_idx = ngs->n_frame - 1;
00438     end_bpidx = ngs->bp_table_idx[frame_idx];
00439 
00440     best_score = WORST_SCORE;
00441     best_exit = NO_BP;
00442 
00443     /* Scan back to find a frame with some backpointers in it. */
00444     while (frame_idx >= 0 && ngs->bp_table_idx[frame_idx] == end_bpidx)
00445         --frame_idx;
00446     /* This is NOT an error, it just means there is no hypothesis yet. */
00447     if (frame_idx < 0)
00448         return NO_BP;
00449 
00450     /* Now find the entry for </s> OR the best scoring entry. */
00451     assert(end_bpidx < ngs->bp_table_size);
00452     for (bp = ngs->bp_table_idx[frame_idx]; bp < end_bpidx; ++bp) {
00453         if (ngs->bp_table[bp].wid == ps_search_finish_wid(ngs)
00454             || ngs->bp_table[bp].score BETTER_THAN best_score) {
00455             best_score = ngs->bp_table[bp].score;
00456             best_exit = bp;
00457         }
00458         if (ngs->bp_table[bp].wid == ps_search_finish_wid(ngs))
00459             break;
00460     }
00461 
00462     if (out_best_score) *out_best_score = best_score;
00463     return best_exit;
00464 }
00465 
00466 char const *
00467 ngram_search_bp_hyp(ngram_search_t *ngs, int bpidx)
00468 {
00469     ps_search_t *base = ps_search_base(ngs);
00470     char *c;
00471     size_t len;
00472     int bp;
00473 
00474     if (bpidx == NO_BP)
00475         return NULL;
00476 
00477     bp = bpidx;
00478     len = 0;
00479     while (bp != NO_BP) {
00480         bptbl_t *be = &ngs->bp_table[bp];
00481         bp = be->bp;
00482         if (s3dict_real_word(ps_search_dict(ngs), be->wid))
00483             len += strlen(s3dict_basestr(ps_search_dict(ngs), be->wid)) + 1;
00484     }
00485 
00486     ckd_free(base->hyp_str);
00487     base->hyp_str = ckd_calloc(1, len);
00488     bp = bpidx;
00489     c = base->hyp_str + len - 1;
00490     while (bp != NO_BP) {
00491         bptbl_t *be = &ngs->bp_table[bp];
00492         size_t len;
00493 
00494         bp = be->bp;
00495         if (s3dict_real_word(ps_search_dict(ngs), be->wid)) {
00496             len = strlen(s3dict_basestr(ps_search_dict(ngs), be->wid));
00497             c -= len;
00498             memcpy(c, s3dict_basestr(ps_search_dict(ngs), be->wid), len);
00499             if (c > base->hyp_str) {
00500                 --c;
00501                 *c = ' ';
00502             }
00503         }
00504     }
00505 
00506     return base->hyp_str;
00507 }
00508 
00509 void
00510 ngram_search_alloc_all_rc(ngram_search_t *ngs, int32 w)
00511 {
00512     chan_t *hmm, *thmm;
00513     xwdssid_t *rssid;
00514     int32 i;
00515 
00516     /* DICT2PID */
00517     /* Get pointer to array of triphones for final diphone. */
00518     assert(s3dict_pronlen(ps_search_dict(ngs), w) > 1);
00519     rssid = dict2pid_rssid(ps_search_dict2pid(ngs),
00520                            s3dict_last_phone(ps_search_dict(ngs),w),
00521                            s3dict_second_last_phone(ps_search_dict(ngs),w));
00522     hmm = ngs->word_chan[w];
00523     if ((hmm == NULL) || (hmm_nonmpx_ssid(&hmm->hmm) != rssid->ssid[0])) {
00524         hmm = listelem_malloc(ngs->chan_alloc);
00525         hmm->next = ngs->word_chan[w];
00526         ngs->word_chan[w] = hmm;
00527 
00528         hmm->info.rc_id = 0;
00529         hmm->ciphone = s3dict_last_phone(ps_search_dict(ngs),w);
00530         hmm_init(ngs->hmmctx, &hmm->hmm, FALSE, rssid->ssid[0], hmm->ciphone);
00531         E_DEBUG(3,("allocated rc_id 0 ssid %d ciphone %d lc %d word %s\n",
00532                    rssid->ssid[0], hmm->ciphone,
00533                    s3dict_second_last_phone(ps_search_dict(ngs),w),
00534                    s3dict_wordstr(ps_search_dict(ngs),w)));
00535     }
00536     for (i = 1; i < rssid->n_ssid; ++i) {
00537         if ((hmm->next == NULL) || (hmm_nonmpx_ssid(&hmm->next->hmm) != rssid->ssid[i])) {
00538             thmm = listelem_malloc(ngs->chan_alloc);
00539             thmm->next = hmm->next;
00540             hmm->next = thmm;
00541             hmm = thmm;
00542 
00543             hmm->info.rc_id = i;
00544             hmm->ciphone = s3dict_last_phone(ps_search_dict(ngs),w);
00545             hmm_init(ngs->hmmctx, &hmm->hmm, FALSE, rssid->ssid[i], hmm->ciphone);
00546             E_DEBUG(3,("allocated rc_id %d ssid %d ciphone %d lc %d word %s\n",
00547                        i, rssid->ssid[i], hmm->ciphone,
00548                        s3dict_second_last_phone(ps_search_dict(ngs),w),
00549                        s3dict_wordstr(ps_search_dict(ngs),w)));
00550         }
00551         else
00552             hmm = hmm->next;
00553     }
00554 }
00555 
00556 void
00557 ngram_search_free_all_rc(ngram_search_t *ngs, int32 w)
00558 {
00559     chan_t *hmm, *thmm;
00560 
00561     for (hmm = ngs->word_chan[w]; hmm; hmm = thmm) {
00562         thmm = hmm->next;
00563         hmm_deinit(&hmm->hmm);
00564         listelem_free(ngs->chan_alloc, hmm);
00565     }
00566     ngs->word_chan[w] = NULL;
00567 }
00568 
00569 int32
00570 ngram_search_exit_score(ngram_search_t *ngs, bptbl_t *pbe, int rcphone)
00571 {
00572     /* DICT2PID */
00573     /* Get the mapping from right context phone ID to index in the
00574      * right context table and the bscore_stack. */
00575     if (pbe->last2_phone == -1) {
00576         /* No right context for single phone predecessor words. */
00577         return ngs->bscore_stack[pbe->s_idx];
00578     }
00579     else {
00580         xwdssid_t *rssid;
00581         /* Find the index for the last diphone of the previous word +
00582          * the first phone of the current word. */
00583         rssid = dict2pid_rssid(ps_search_dict2pid(ngs),
00584                                pbe->last_phone, pbe->last2_phone);
00585         return ngs->bscore_stack[pbe->s_idx + rssid->cimap[rcphone]];
00586     }
00587 }
00588 
00589 /*
00590  * Compute acoustic and LM scores for a BPTable entry (segment).
00591  */
00592 void
00593 ngram_compute_seg_score(ngram_search_t *ngs, bptbl_t *be, float32 lwf,
00594                         int32 *out_ascr, int32 *out_lscr)
00595 {
00596     bptbl_t *pbe;
00597     int32 start_score;
00598 
00599     /* Start of utterance. */
00600     if (be->bp == NO_BP) {
00601         *out_ascr = be->score;
00602         *out_lscr = 0;
00603         return;
00604     }
00605 
00606     /* Otherwise, calculate lscr and ascr. */
00607     pbe = ngs->bp_table + be->bp;
00608     start_score = ngram_search_exit_score(ngs, pbe,
00609                                  s3dict_first_phone(ps_search_dict(ngs),be->wid));
00610 
00611     /* FIXME: WORST_SCORE shouldn't propagate, but sometimes it
00612        does.  We cannot allow it to go any further because that
00613        will result in positive acoustic scores.  Tracing the
00614        source of this may be a bit involved. */
00615     if (start_score == WORST_SCORE)
00616         start_score = 0;
00617 
00618     /* FIXME: These result in positive acoustic scores when filler
00619        words have non-filler pronunciations.  That whole business
00620        is still pretty much broken but at least it doesn't
00621        segfault. */
00622     if (be->wid == ps_search_silence_wid(ngs)) {
00623         *out_lscr = ngs->silpen;
00624     }
00625     else if (s3dict_filler_word(ps_search_dict(ngs), be->wid)) {
00626         *out_lscr = ngs->fillpen;
00627     }
00628     else {
00629         int32 n_used;
00630         *out_lscr = ngram_tg_score(ngs->lmset,
00631                                    be->real_wid,
00632                                    pbe->real_wid,
00633                                    pbe->prev_real_wid, &n_used);
00634         *out_lscr = *out_lscr * lwf;
00635     }
00636     *out_ascr = be->score - start_score - *out_lscr;
00637 }
00638 
00639 static int
00640 ngram_search_start(ps_search_t *search)
00641 {
00642     ngram_search_t *ngs = (ngram_search_t *)search;
00643 
00644     ngs->done = FALSE;
00645     ngram_model_flush(ngs->lmset);
00646     if (ngs->fwdtree)
00647         ngram_fwdtree_start(ngs);
00648     else if (ngs->fwdflat)
00649         ngram_fwdflat_start(ngs);
00650     else
00651         return -1;
00652     return 0;
00653 }
00654 
00655 static int
00656 ngram_search_step(ps_search_t *search, int frame_idx)
00657 {
00658     ngram_search_t *ngs = (ngram_search_t *)search;
00659 
00660     if (ngs->fwdtree)
00661         return ngram_fwdtree_search(ngs, frame_idx);
00662     else if (ngs->fwdflat)
00663         return ngram_fwdflat_search(ngs, frame_idx);
00664     else
00665         return -1;
00666 }
00667 
00668 static void
00669 dump_bptable(ngram_search_t *ngs)
00670 {
00671     int i;
00672     E_INFO("Backpointer table (%d entries):\n", ngs->bpidx);
00673     for (i = 0; i < ngs->bpidx; ++i) {
00674         E_INFO_NOFN("%-5d %-10s start %-3d end %-3d score %-8d bp\n", /* %-3d history %08x\n", */
00675                     i, s3dict_wordstr(ps_search_dict(ngs), ngs->bp_table[i].wid),
00676                     ngs->bp_table[i].bp == -1 ? 0 : 
00677                     ngs->bp_table[ngs->bp_table[i].bp].frame + 1,
00678                     ngs->bp_table[i].frame,
00679                     ngs->bp_table[i].score,
00680                     ngs->bp_table[i].bp);
00681         /* ngs->bp_table[i].hist_hash); */
00682     }
00683 }
00684 
00685 static int
00686 ngram_search_finish(ps_search_t *search)
00687 {
00688     ngram_search_t *ngs = (ngram_search_t *)search;
00689 
00690     if (ngs->fwdtree) {
00691         ngram_fwdtree_finish(ngs);
00692         /* dump_bptable(ngs); */
00693 
00694         /* Now do fwdflat search in its entirety, if requested. */
00695         if (ngs->fwdflat) {
00696             int i;
00697             /* Rewind the acoustic model. */
00698             if (acmod_rewind(ps_search_acmod(ngs)) < 0)
00699                 return -1;
00700             /* Now redo search. */
00701             ngram_fwdflat_start(ngs);
00702             i = 0;
00703             while (ps_search_acmod(ngs)->n_feat_frame > 0) {
00704                 int nfr;
00705                 if ((nfr = ngram_fwdflat_search(ngs, i)) < 0)
00706                     return nfr;
00707                 acmod_advance(ps_search_acmod(ngs));
00708                 ++i;
00709             }
00710             ngram_fwdflat_finish(ngs);
00711             /* And now, we should have a result... */
00712             /* dump_bptable(ngs); */
00713         }
00714     }
00715     else if (ngs->fwdflat) {
00716         ngram_fwdflat_finish(ngs);
00717     }
00718 
00719     /* Mark the current utterance as done. */
00720     ngs->done = TRUE;
00721     return 0;
00722 }
00723 
00724 static ps_latlink_t *
00725 ngram_search_bestpath(ps_search_t *search, int32 *out_score, int backward)
00726 {
00727     ngram_search_t *ngs = (ngram_search_t *)search;
00728 
00729     if (search->last_link == NULL) {
00730         search->last_link = ps_lattice_bestpath(search->dag, ngs->lmset,
00731                                                 ngs->bestpath_fwdtree_lw_ratio,
00732                                                 ngs->ascale);
00733         if (search->last_link == NULL)
00734             return NULL;
00735         /* Also calculate betas so we can fill in the posterior
00736          * probability field in the segmentation. */
00737         if (search->post == 0)
00738             search->post = ps_lattice_posterior(search->dag, ngs->lmset,
00739                                                 ngs->ascale);
00740     }
00741     if (out_score)
00742         *out_score = search->last_link->path_scr + search->dag->final_node_ascr;
00743     return search->last_link;
00744 }
00745 
00746 static char const *
00747 ngram_search_hyp(ps_search_t *search, int32 *out_score)
00748 {
00749     ngram_search_t *ngs = (ngram_search_t *)search;
00750 
00751     /* Only do bestpath search if the utterance is complete. */
00752     if (ngs->bestpath && ngs->done) {
00753         ps_lattice_t *dag;
00754         ps_latlink_t *link;
00755 
00756         if ((dag = ngram_search_lattice(search)) == NULL)
00757             return NULL;
00758         if ((link = ngram_search_bestpath(search, out_score, FALSE)) == NULL)
00759             return NULL;
00760         return ps_lattice_hyp(dag, link);
00761     }
00762     else {
00763         int32 bpidx;
00764 
00765         /* fwdtree and fwdflat use same backpointer table. */
00766         bpidx = ngram_search_find_exit(ngs, -1, out_score);
00767         if (bpidx != NO_BP)
00768             return ngram_search_bp_hyp(ngs, bpidx);
00769     }
00770 
00771     return NULL;
00772 }
00773 
00774 static void
00775 ngram_search_bp2itor(ps_seg_t *seg, int bp)
00776 {
00777     ngram_search_t *ngs = (ngram_search_t *)seg->search;
00778     bptbl_t *be, *pbe;
00779 
00780     be = &ngs->bp_table[bp];
00781     pbe = be->bp == -1 ? NULL : &ngs->bp_table[be->bp];
00782     seg->word = s3dict_wordstr(ps_search_dict(ngs), be->wid);
00783     seg->ef = be->frame;
00784     seg->sf = pbe ? pbe->frame + 1 : 0;
00785     seg->prob = 0; /* Bogus value... */
00786     /* Compute acoustic and LM scores for this segment. */
00787     if (pbe == NULL) {
00788         seg->ascr = be->score;
00789         seg->lscr = 0;
00790         seg->lback = 0;
00791     }
00792     else {
00793         int32 start_score;
00794 
00795         /* Find ending path score of previous word. */
00796         start_score = ngram_search_exit_score(ngs, pbe,
00797                                      s3dict_first_phone(ps_search_dict(ngs), be->wid));
00798         if (be->wid == ps_search_silence_wid(ngs)) {
00799             seg->lscr = ngs->silpen;
00800         }
00801         else if (s3dict_filler_word(ps_search_dict(ngs), be->wid)) {
00802             seg->lscr = ngs->fillpen;
00803         }
00804         else {
00805             seg->lscr = ngram_tg_score(ngs->lmset,
00806                                        be->real_wid,
00807                                        pbe->real_wid,
00808                                        pbe->prev_real_wid, &seg->lback);
00809             seg->lscr = (int32)(seg->lscr * seg->lwf);
00810         }
00811         seg->ascr = be->score - start_score - seg->lscr;
00812     }
00813 }
00814 
00815 static void
00816 ngram_bp_seg_free(ps_seg_t *seg)
00817 {
00818     bptbl_seg_t *itor = (bptbl_seg_t *)seg;
00819     
00820     ckd_free(itor->bpidx);
00821     ckd_free(itor);
00822 }
00823 
00824 static ps_seg_t *
00825 ngram_bp_seg_next(ps_seg_t *seg)
00826 {
00827     bptbl_seg_t *itor = (bptbl_seg_t *)seg;
00828 
00829     if (++itor->cur == itor->n_bpidx) {
00830         ngram_bp_seg_free(seg);
00831         return NULL;
00832     }
00833 
00834     ngram_search_bp2itor(seg, itor->bpidx[itor->cur]);
00835     return seg;
00836 }
00837 
00838 static ps_segfuncs_t ngram_bp_segfuncs = {
00839     /* seg_next */ ngram_bp_seg_next,
00840     /* seg_free */ ngram_bp_seg_free
00841 };
00842 
00843 static ps_seg_t *
00844 ngram_search_bp_iter(ngram_search_t *ngs, int bpidx, float32 lwf)
00845 {
00846     bptbl_seg_t *itor;
00847     int bp, cur;
00848 
00849     /* Calling this an "iterator" is a bit of a misnomer since we have
00850      * to get the entire backtrace in order to produce it.  On the
00851      * other hand, all we actually need is the bptbl IDs, and we can
00852      * allocate a fixed-size array of them. */
00853     itor = ckd_calloc(1, sizeof(*itor));
00854     itor->base.vt = &ngram_bp_segfuncs;
00855     itor->base.search = ps_search_base(ngs);
00856     itor->base.lwf = lwf;
00857     itor->n_bpidx = 0;
00858     bp = bpidx;
00859     while (bp != NO_BP) {
00860         bptbl_t *be = &ngs->bp_table[bp];
00861         bp = be->bp;
00862         ++itor->n_bpidx;
00863     }
00864     if (itor->n_bpidx == 0) {
00865         ckd_free(itor);
00866         return NULL;
00867     }
00868     itor->bpidx = ckd_calloc(itor->n_bpidx, sizeof(*itor->bpidx));
00869     cur = itor->n_bpidx - 1;
00870     bp = bpidx;
00871     while (bp != NO_BP) {
00872         bptbl_t *be = &ngs->bp_table[bp];
00873         itor->bpidx[cur] = bp;
00874         bp = be->bp;
00875         --cur;
00876     }
00877 
00878     /* Fill in relevant fields for first element. */
00879     ngram_search_bp2itor((ps_seg_t *)itor, itor->bpidx[0]);
00880 
00881     return (ps_seg_t *)itor;
00882 }
00883 
00884 static ps_seg_t *
00885 ngram_search_seg_iter(ps_search_t *search, int32 *out_score)
00886 {
00887     ngram_search_t *ngs = (ngram_search_t *)search;
00888 
00889     /* Only do bestpath search if the utterance is done. */
00890     if (ngs->bestpath && ngs->done) {
00891         ps_lattice_t *dag;
00892         ps_latlink_t *link;
00893 
00894         if ((dag = ngram_search_lattice(search)) == NULL)
00895             return NULL;
00896         if ((link = ngram_search_bestpath(search, out_score, TRUE)) == NULL)
00897             return NULL;
00898         return ps_lattice_seg_iter(dag, link,
00899                                    ngs->bestpath_fwdtree_lw_ratio);
00900     }
00901     else {
00902         int32 bpidx;
00903 
00904         /* fwdtree and fwdflat use same backpointer table. */
00905         bpidx = ngram_search_find_exit(ngs, -1, out_score);
00906         return ngram_search_bp_iter(ngs, bpidx,
00907                                     /* but different language weights... */
00908                                     (ngs->done && ngs->fwdflat)
00909                                     ? ngs->fwdflat_fwdtree_lw_ratio : 1.0);
00910     }
00911 
00912     return NULL;
00913 }
00914 
00915 static int32
00916 ngram_search_prob(ps_search_t *search)
00917 {
00918     ngram_search_t *ngs = (ngram_search_t *)search;
00919 
00920     /* Only do bestpath search if the utterance is done. */
00921     if (ngs->bestpath && ngs->done) {
00922         ps_lattice_t *dag;
00923         ps_latlink_t *link;
00924 
00925         if ((dag = ngram_search_lattice(search)) == NULL)
00926             return 0;
00927         if ((link = ngram_search_bestpath(search, NULL, TRUE)) == NULL)
00928             return 0;
00929         return search->post;
00930     }
00931     else {
00932         /* FIXME: Give some kind of good estimate here, eventually. */
00933         return 0;
00934     }
00935 }
00936 
00937 static void
00938 create_dag_nodes(ngram_search_t *ngs, ps_lattice_t *dag)
00939 {
00940     bptbl_t *bp_ptr;
00941     int32 i;
00942 
00943     for (i = 0, bp_ptr = ngs->bp_table; i < ngs->bpidx; ++i, ++bp_ptr) {
00944         int32 sf, ef, wid;
00945         ps_latnode_t *node;
00946 
00947         /* Skip invalid backpointers (these result from -maxwpf pruning) */
00948         if (!bp_ptr->valid)
00949             continue;
00950 
00951         sf = (bp_ptr->bp < 0) ? 0 : ngs->bp_table[bp_ptr->bp].frame + 1;
00952         ef = bp_ptr->frame;
00953         wid = bp_ptr->wid;
00954 
00955         assert(ef < dag->n_frames);
00956         /* Skip non-final </s> entries. */
00957         if ((wid == ps_search_finish_wid(ngs)) && (ef < dag->n_frames - 1))
00958             continue;
00959 
00960         /* Skip if word not in LM */
00961         if ((!s3dict_filler_word(ps_search_dict(ngs), wid))
00962             && (!ngram_model_set_known_wid(ngs->lmset,
00963                                            s3dict_basewid(ps_search_dict(ngs), wid))))
00964             continue;
00965 
00966         /* See if bptbl entry <wid,sf> already in lattice */
00967         for (node = dag->nodes; node; node = node->next) {
00968             if ((node->wid == wid) && (node->sf == sf))
00969                 break;
00970         }
00971 
00972         /* For the moment, store bptbl indices in node.{fef,lef} */
00973         if (node)
00974             node->lef = i;
00975         else {
00976             /* New node; link to head of list */
00977             node = listelem_malloc(dag->latnode_alloc);
00978             node->wid = wid;
00979             node->sf = sf; /* This is a frame index. */
00980             node->fef = node->lef = i; /* These are backpointer indices (argh) */
00981             node->reachable = FALSE;
00982             node->entries = NULL;
00983             node->exits = NULL;
00984 
00985             node->next = dag->nodes;
00986             dag->nodes = node;
00987         }
00988     }
00989 }
00990 
00991 static ps_latnode_t *
00992 find_start_node(ngram_search_t *ngs, ps_lattice_t *dag)
00993 {
00994     ps_latnode_t *node;
00995 
00996     /* Find start node <s>.0 */
00997     for (node = dag->nodes; node; node = node->next) {
00998         if ((node->wid == ps_search_start_wid(ngs)) && (node->sf == 0))
00999             break;
01000     }
01001     if (!node) {
01002         /* This is probably impossible. */
01003         E_ERROR("Couldn't find <s> in first frame\n");
01004         return NULL;
01005     }
01006     return node;
01007 }
01008 
01009 static ps_latnode_t *
01010 find_end_node(ngram_search_t *ngs, ps_lattice_t *dag, float32 lwf)
01011 {
01012     ps_latnode_t *node;
01013     int32 ef, bestbp, bp, bestscore;
01014 
01015     /* Find final node </s>.last_frame; nothing can follow this node */
01016     for (node = dag->nodes; node; node = node->next) {
01017         int32 lef = ngs->bp_table[node->lef].frame;
01018         if ((node->wid == ps_search_finish_wid(ngs))
01019             && (lef == dag->n_frames - 1))
01020             break;
01021     }
01022     if (node != NULL)
01023         return node;
01024 
01025     /* It is quite likely that no </s> exited in the last frame.  So,
01026      * find the node corresponding to the best exit. */
01027     /* Find the last frame containing a word exit. */
01028     for (ef = dag->n_frames - 1;
01029          ef >= 0 && ngs->bp_table_idx[ef] == ngs->bpidx;
01030          --ef);
01031     if (ef < 0) {
01032         E_ERROR("Empty backpointer table: can not build DAG.\n");
01033         return NULL;
01034     }
01035 
01036     /* Find best word exit in that frame. */
01037     bestscore = WORST_SCORE;
01038     bestbp = NO_BP;
01039     for (bp = ngs->bp_table_idx[ef]; bp < ngs->bp_table_idx[ef + 1]; ++bp) {
01040         int32 n_used, l_scr;
01041         l_scr = ngram_tg_score(ngs->lmset, ps_search_finish_wid(ngs),
01042                                ngs->bp_table[bp].real_wid,
01043                                ngs->bp_table[bp].prev_real_wid,
01044                                &n_used);
01045         l_scr = l_scr * lwf;
01046 
01047         if (ngs->bp_table[bp].score + l_scr BETTER_THAN bestscore) {
01048             bestscore = ngs->bp_table[bp].score + l_scr;
01049             bestbp = bp;
01050         }
01051     }
01052     if (bestbp == NO_BP) {
01053         E_ERROR("No word exits found in last frame, assuming no recognition\n");
01054         return NULL;
01055     }
01056     E_WARN("</s> not found in last frame, using %s instead\n",
01057            s3dict_basestr(ps_search_dict(ngs), ngs->bp_table[bestbp].wid));
01058 
01059     /* Now find the node that corresponds to it. */
01060     for (node = dag->nodes; node; node = node->next) {
01061         if (node->lef == bestbp)
01062             return node;
01063     }
01064 
01065     /* FIXME: This seems to happen a lot! */
01066     E_ERROR("Failed to find DAG node corresponding to %s\n",
01067            s3dict_basestr(ps_search_dict(ngs), ngs->bp_table[bestbp].wid));
01068     return NULL;
01069 }
01070 
01071 /*
01072  * Build lattice from bptable.
01073  */
01074 ps_lattice_t *
01075 ngram_search_lattice(ps_search_t *search)
01076 {
01077     int32 i, ef, lef, score, ascr, lscr;
01078     ps_latnode_t *node, *from, *to;
01079     ngram_search_t *ngs;
01080     ps_lattice_t *dag;
01081     float lwf;
01082 
01083     ngs = (ngram_search_t *)search;
01084 
01085     /* Check to see if a lattice has previously been created over the
01086      * same number of frames, and reuse it if so. */
01087     if (search->dag && search->dag->n_frames == ngs->n_frame)
01088         return search->dag;
01089 
01090     /* Nope, create a new one. */
01091     ps_lattice_free(search->dag);
01092     search->dag = NULL;
01093     dag = ps_lattice_init_search(search, ngs->n_frame);
01094     /* Compute these such that they agree with the fwdtree language weight. */
01095     lwf = ngs->fwdflat ? ngs->fwdflat_fwdtree_lw_ratio : 1.0;
01096     create_dag_nodes(ngs, dag);
01097     if ((dag->start = find_start_node(ngs, dag)) == NULL)
01098         goto error_out;
01099     if ((dag->end = find_end_node(ngs, dag, ngs->bestpath_fwdtree_lw_ratio)) == NULL)
01100         goto error_out;
01101     E_INFO("lattice start node %s.%d end node %s.%d\n",
01102            s3dict_wordstr(search->dict, dag->start->wid), dag->start->sf,
01103            s3dict_wordstr(search->dict, dag->end->wid), dag->end->sf);
01104 
01105     ngram_compute_seg_score(ngs, ngs->bp_table + dag->end->lef, lwf,
01106                             &dag->final_node_ascr, &lscr);
01107 
01108     /*
01109      * At this point, dag->nodes is ordered such that nodes earlier in
01110      * the list can follow (in time) those later in the list, but not
01111      * vice versa.  Now create precedence links and simultanesously
01112      * mark all nodes that can reach dag->end.  (All nodes are reached
01113      * from dag->start; no problem there.)
01114      */
01115     dag->end->reachable = TRUE;
01116     for (to = dag->end; to; to = to->next) {
01117         /* Skip if not reachable; it will never be reachable from dag->end */
01118         if (!to->reachable)
01119             continue;
01120 
01121         /* Find predecessors of to : from->fef+1 <= to->sf <= from->lef+1 */
01122         for (from = to->next; from; from = from->next) {
01123             bptbl_t *bp_ptr;
01124 
01125             ef = ngs->bp_table[from->fef].frame;
01126             lef = ngs->bp_table[from->lef].frame;
01127 
01128             if ((to->sf <= ef) || (to->sf > lef + 1))
01129                 continue;
01130 
01131             /* Find bptable entry for "from" that exactly precedes "to" */
01132             i = from->fef;
01133             bp_ptr = ngs->bp_table + i;
01134             for (; i <= from->lef; i++, bp_ptr++) {
01135                 if (bp_ptr->wid != from->wid)
01136                     continue;
01137                 if (bp_ptr->frame >= to->sf - 1)
01138                     break;
01139             }
01140 
01141             if ((i > from->lef) || (bp_ptr->frame != to->sf - 1))
01142                 continue;
01143 
01144             /* Find acoustic score from.sf->to.sf-1 with right context = to */
01145             ngram_compute_seg_score(ngs, bp_ptr, lwf,
01146                                     &ascr, &lscr);
01147             /* Remove context score calculated above (FIXME: just don't calculate it...) */
01148             score = (ngram_search_exit_score(ngs, bp_ptr,
01149                                     s3dict_first_phone(ps_search_dict(ngs), to->wid))
01150                      - bp_ptr->score + ascr);
01151             if (score BETTER_THAN 0) {
01152                 /* Scores must be negative, or Bad Things will happen.
01153                    In general, they are, except in corner cases
01154                    involving filler words.  We don't want to throw any
01155                    links away so we'll keep these, but with some
01156                    arbitrarily improbable but recognizable score. */
01157                 ps_lattice_link(dag, from, to, -424242, bp_ptr->frame);
01158                 from->reachable = TRUE;
01159             }
01160             else if (score BETTER_THAN WORST_SCORE) {
01161                 ps_lattice_link(dag, from, to, score, bp_ptr->frame);
01162                 from->reachable = TRUE;
01163             }
01164         }
01165     }
01166 
01167     /* There must be at least one path between dag->start and dag->end */
01168     if (!dag->start->reachable) {
01169         E_ERROR("End node of lattice isolated; unreachable\n");
01170         goto error_out;
01171     }
01172 
01173     for (node = dag->nodes; node; node = node->next) {
01174         /* Change node->{fef,lef} from bptbl indices to frames. */
01175         node->fef = ngs->bp_table[node->fef].frame;
01176         node->lef = ngs->bp_table[node->lef].frame;
01177         /* Find base wid for nodes. */
01178         node->basewid = s3dict_basewid(search->dict, node->wid);
01179     }
01180 
01181     /* Link nodes with alternate pronunciations at the same timepoint. */
01182     for (node = dag->nodes; node; node = node->next) {
01183         ps_latnode_t *alt;
01184         /* Scan forward to find the next alternate, then stop. */
01185         for (alt = node->next; alt && alt->sf == node->sf; alt = alt->next) {
01186             if (alt->basewid == node->basewid) {
01187                 alt->alt = node->alt;
01188                 node->alt = alt;
01189                 break;
01190             }
01191         }
01192     }
01193 
01194     /* Minor hack: If the final node is a filler word and not </s>,
01195      * then set its base word ID to </s>, so that the language model
01196      * scores won't be screwed up. */
01197     if (s3dict_filler_word(ps_search_dict(ngs), dag->end->wid))
01198         dag->end->basewid = ps_search_finish_wid(ngs);
01199 
01200     /* Free nodes unreachable from dag->end and their links */
01201     ps_lattice_delete_unreachable(dag);
01202 
01203     /* Build links around silence and filler words, since they do not
01204      * exist in the language model. */
01205     ps_lattice_bypass_fillers(dag, ngs->silpen, ngs->fillpen);
01206 
01207     search->dag = dag;
01208     return dag;
01209 
01210 error_out:
01211     ps_lattice_free(dag);
01212     return NULL;
01213 }

Generated on Mon Jan 24 21:50:16 2011 for PocketSphinx by  doxygen 1.4.7