fsg_lextree.c
Go to the documentation of this file.
/* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* ====================================================================
* Copyright (c) 1999-2010 Carnegie Mellon University. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
* NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ====================================================================
*
*/
/* System headers. */
#include <stdio.h>
#include <string.h>
#include <assert.h>
/* SphinxBase headers. */
#include <sphinxbase/ckd_alloc.h>
#include <sphinxbase/err.h>
/* Local headers. */
#include "fsg_lextree.h"
#define __FSG_DBG__ 0
/* A linklist structure that is actually used to build local lextrees at grammar nodes */
typedef struct fsg_glist_linklist_t {
int32 ci, rc;
glist_t glist;
struct fsg_glist_linklist_t *next;
} fsg_glist_linklist_t;
static fsg_pnode_t *fsg_psubtree_init(fsg_lextree_t *tree,
fsg_model_t *fsg,
int32 from_state,
fsg_pnode_t **alloc_head);
static void fsg_psubtree_free(fsg_pnode_t *alloc_head);
static void fsg_psubtree_dump(fsg_lextree_t *tree, fsg_pnode_t *root, FILE *fp);
static void
fsg_lextree_lc_rc(fsg_lextree_t *lextree)
{
int32 s, i, j;
int32 n_ci;
fsg_model_t *fsg;
int32 silcipid;
int32 len;
silcipid = bin_mdef_silphone(lextree->mdef);
assert(silcipid >= 0);
n_ci = bin_mdef_n_ciphone(lextree->mdef);
fsg = lextree->fsg;
/*
* lextree->lc[s] = set of left context CIphones for state s. Similarly, rc[s]
* for right context CIphones.
*/
lextree->lc = ckd_calloc_2d(fsg->n_state, n_ci + 1, sizeof(**lextree->lc));
lextree->rc = ckd_calloc_2d(fsg->n_state, n_ci + 1, sizeof(**lextree->rc));
E_INFO("Allocated %d bytes (%d KiB) for left and right context phones\n",
fsg->n_state * (n_ci + 1) * 2,
fsg->n_state * (n_ci + 1) * 2 / 1024);
for (s = 0; s < fsg->n_state; s++) {
fsg_arciter_t *itor;
for (itor = fsg_model_arcs(fsg, s); itor; itor = fsg_arciter_next(itor)) {
fsg_link_t *l = fsg_arciter_get(itor);
int32 dictwid;
if (fsg_link_wid(l) >= 0) {
dictwid = dict_wordid(lextree->dict,
fsg_model_word_str(lextree->fsg, l->wid));
/*
* Add the first CIphone of l->wid to the rclist of state s, and
* the last CIphone to lclist of state d.
* (Filler phones are a pain to deal with. There is no direct
* marking of a filler phone; but only filler words are supposed to
* use such phones, so we use that fact. HACK!! FRAGILE!!)
*
* UPD: tests carsh here if .fsg model used with wrong hmm and
* dictionary
*/
if (fsg_model_is_filler(fsg, fsg_link_wid(l))) {
/* Filler phone; use silence phone as context */
lextree->rc[fsg_link_from_state(l)][silcipid] = 1;
lextree->lc[fsg_link_to_state(l)][silcipid] = 1;
}
else {
len = dict_pronlen(lextree->dict, dictwid);
lextree->rc[fsg_link_from_state(l)][dict_pron(lextree->dict, dictwid, 0)] = 1;
lextree->lc[fsg_link_to_state(l)][dict_pron(lextree->dict, dictwid, len - 1)] = 1;
}
}
}
}
for (s = 0; s < fsg->n_state; s++) {
/*
* Add SIL phone to the lclist and rclist of each state. Strictly
* speaking, only needed at start and final states, respectively, but
* all states considered since the user may change the start and final
* states. In any case, most applications would have a silence self
* loop at each state, hence these would be needed anyway.
*/
lextree->lc[s][silcipid] = 1;
lextree->rc[s][silcipid] = 1;
}
/*
* Propagate lc and rc lists past null transitions. (Since FSG contains
* null transitions closure, no need to worry about a chain of successive
* null transitions. Right??)
*
* This can't be joined with the previous loop because we first calculate
* contexts and only then we can propagate them.
*/
for (s = 0; s < fsg->n_state; s++) {
fsg_arciter_t *itor;
for (itor = fsg_model_arcs(fsg, s); itor; itor = fsg_arciter_next(itor)) {
fsg_link_t *l = fsg_arciter_get(itor);
if (fsg_link_wid(l) < 0) {
/*
* lclist(d) |= lclist(s), because all the words ending up at s, can
* now also end at d, becoming the left context for words leaving d.
*/
for (i = 0; i < n_ci; i++)
lextree->lc[fsg_link_to_state(l)][i] |= lextree->lc[fsg_link_from_state(l)][i];
/*
* Similarly, rclist(s) |= rclist(d), because all the words leaving d
* can equivalently leave s, becoming the right context for words
* ending up at s.
*/
for (i = 0; i < n_ci; i++)
lextree->rc[fsg_link_from_state(l)][i] |= lextree->rc[fsg_link_to_state(l)][i];
}
}
}
/* Convert the bit-vector representation into a list */
for (s = 0; s < fsg->n_state; s++) {
j = 0;
for (i = 0; i < n_ci; i++) {
if (lextree->lc[s][i]) {
lextree->lc[s][j] = i;
j++;
}
}
lextree->lc[s][j] = -1; /* Terminate the list */
j = 0;
for (i = 0; i < n_ci; i++) {
if (lextree->rc[s][i]) {
lextree->rc[s][j] = i;
j++;
}
}
lextree->rc[s][j] = -1; /* Terminate the list */
}
}
/*
* For now, allocate the entire lextree statically.
*/
fsg_lextree_t *
fsg_lextree_init(fsg_model_t * fsg, dict_t *dict, dict2pid_t *d2p,
bin_mdef_t *mdef, hmm_context_t *ctx,
int32 wip, int32 pip)
{
int32 s, n_leaves;
fsg_lextree_t *lextree;
fsg_pnode_t *pn;
lextree = ckd_calloc(1, sizeof(fsg_lextree_t));
lextree->fsg = fsg;
lextree->root = ckd_calloc(fsg_model_n_state(fsg),
sizeof(fsg_pnode_t *));
lextree->alloc_head = ckd_calloc(fsg_model_n_state(fsg),
sizeof(fsg_pnode_t *));
lextree->ctx = ctx;
lextree->dict = dict;
lextree->d2p = d2p;
lextree->mdef = mdef;
lextree->wip = wip;
lextree->pip = pip;
/* Compute lc and rc for fsg. */
fsg_lextree_lc_rc(lextree);
/* Create lextree for each state, i.e. an HMM network that
* represents words for all arcs exiting that state. Note that
* for a dense grammar such as an N-gram model, this will
* rapidly exhaust all available memory. */
lextree->n_pnode = 0;
n_leaves = 0;
for (s = 0; s < fsg_model_n_state(fsg); s++) {
lextree->root[s] =
fsg_psubtree_init(lextree, fsg, s, &(lextree->alloc_head[s]));
for (pn = lextree->alloc_head[s]; pn; pn = pn->alloc_next) {
lextree->n_pnode++;
if (pn->leaf)
++n_leaves;
}
}
E_INFO("%d HMM nodes in lextree (%d leaves)\n",
lextree->n_pnode, n_leaves);
E_INFO("Allocated %d bytes (%d KiB) for all lextree nodes\n",
lextree->n_pnode * sizeof(fsg_pnode_t),
lextree->n_pnode * sizeof(fsg_pnode_t) / 1024);
E_INFO("Allocated %d bytes (%d KiB) for lextree leafnodes\n",
n_leaves * sizeof(fsg_pnode_t),
n_leaves * sizeof(fsg_pnode_t) / 1024);
#if __FSG_DBG__
fsg_lextree_dump(lextree, stdout);
#endif
return lextree;
}
void
fsg_lextree_dump(fsg_lextree_t * lextree, FILE * fp)
{
int32 s;
for (s = 0; s < fsg_model_n_state(lextree->fsg); s++) {
fprintf(fp, "State %5d root %p\n", s, lextree->root[s]);
fsg_psubtree_dump(lextree, lextree->root[s], fp);
}
fflush(fp);
}
void
fsg_lextree_free(fsg_lextree_t * lextree)
{
int32 s;
if (lextree == NULL)
return;
if (lextree->fsg)
for (s = 0; s < fsg_model_n_state(lextree->fsg); s++)
fsg_psubtree_free(lextree->alloc_head[s]);
ckd_free_2d(lextree->lc);
ckd_free_2d(lextree->rc);
ckd_free(lextree->root);
ckd_free(lextree->alloc_head);
ckd_free(lextree);
}
/******************************
* psubtree stuff starts here *
******************************/
void fsg_glist_linklist_free(fsg_glist_linklist_t *glist)
{
if (glist) {
fsg_glist_linklist_t *nxtglist;
if (glist->glist)
glist_free(glist->glist);
nxtglist = glist->next;
while (nxtglist) {
ckd_free(glist);
glist = nxtglist;
if (glist->glist)
glist_free(glist->glist);
nxtglist = glist->next;
}
ckd_free(glist);
}
return;
}
void
fsg_pnode_add_all_ctxt(fsg_pnode_ctxt_t * ctxt)
{
int32 i;
for (i = 0; i < FSG_PNODE_CTXT_BVSZ; i++)
ctxt->bv[i] = 0xffffffff;
}
uint32 fsg_pnode_ctxt_sub_generic(fsg_pnode_ctxt_t *src, fsg_pnode_ctxt_t *sub)
{
int32 i;
uint32 res = 0;
for (i = 0; i < FSG_PNODE_CTXT_BVSZ; i++)
res |= (src->bv[i] = ~(sub->bv[i]) & src->bv[i]);
return res;
}
/*
* fsg_pnode_ctxt_sub(fsg_pnode_ctxt_t * src, fsg_pnode_ctxt_t * sub)
* This has been moved into a macro in fsg_psubtree.h
* because it is called so frequently!
*/
/*
* Add the word emitted by the given transition (fsglink) to the given lextree
* (rooted at root), and return the new lextree root. (There may actually be
* several root nodes, maintained in a linked list via fsg_pnode_t.sibling.
* "root" is the head of this list.)
* lclist, rclist: sets of left and right context phones for this link.
* alloc_head: head of a linear list of all allocated pnodes for the parent
* FSG state, kept elsewhere and updated by this routine.
*/
static fsg_pnode_t *
psubtree_add_trans(fsg_lextree_t *lextree,
fsg_pnode_t * root,
fsg_glist_linklist_t **curglist,
fsg_link_t * fsglink,
int16 *lclist, int16 *rclist,
fsg_pnode_t ** alloc_head)
{
int32 silcipid; /* Silence CI phone ID */
int32 pronlen; /* Pronunciation length */
int32 wid; /* FSG (not dictionary!!) word ID */
int32 dictwid; /* Dictionary (not FSG!!) word ID */
int32 ssid; /* Senone Sequence ID */
int32 tmatid;
gnode_t *gn;
fsg_pnode_t *pnode, *pred, *head;
int32 n_ci, p, lc, rc;
glist_t lc_pnodelist; /* Temp pnodes list for different left contexts */
glist_t rc_pnodelist; /* Temp pnodes list for different right contexts */
int32 i, j;
int n_lc_alloc = 0, n_int_alloc = 0, n_rc_alloc = 0;
silcipid = bin_mdef_silphone(lextree->mdef);
n_ci = bin_mdef_n_ciphone(lextree->mdef);
wid = fsg_link_wid(fsglink);
assert(wid >= 0); /* Cannot be a null transition */
dictwid = dict_wordid(lextree->dict,
fsg_model_word_str(lextree->fsg, wid));
pronlen = dict_pronlen(lextree->dict, dictwid);
assert(pronlen >= 1);
assert(lclist[0] >= 0); /* At least one phonetic context provided */
assert(rclist[0] >= 0);
head = *alloc_head;
pred = NULL;
if (pronlen == 1) { /* Single-phone word */
int ci = dict_first_phone(lextree->dict, dictwid);
/* Only non-filler words are mpx */
if (!dict_filler_word(lextree->dict, dictwid)) {
/*
* Left diphone ID for single-phone words already assumes SIL is right
* context; only left contexts need to be handled.
*/
lc_pnodelist = NULL;
for (i = 0; lclist[i] >= 0; i++) {
lc = lclist[i];
ssid = dict2pid_lrdiph_rc(lextree->d2p, ci, lc, silcipid);
tmatid = bin_mdef_pid2tmatid(lextree->mdef, dict_first_phone(lextree->dict, dictwid));
/* Check if this ssid already allocated for some other context */
for (gn = lc_pnodelist; gn; gn = gnode_next(gn)) {
pnode = (fsg_pnode_t *) gnode_ptr(gn);
if (hmm_nonmpx_ssid(&pnode->hmm) == ssid) {
/* already allocated; share it for this context phone */
fsg_pnode_add_ctxt(pnode, lc);
break;
}
}
if (!gn) { /* ssid not already allocated */
pnode =
(fsg_pnode_t *) ckd_calloc(1, sizeof(fsg_pnode_t));
pnode->ctx = lextree->ctx;
pnode->next.fsglink = fsglink;
pnode->logs2prob =
(fsg_link_logs2prob(fsglink) >> SENSCR_SHIFT)
+ lextree->wip + lextree->pip;
pnode->ci_ext = dict_first_phone(lextree->dict, dictwid);
pnode->ppos = 0;
pnode->leaf = TRUE;
pnode->sibling = root; /* All root nodes linked together */
fsg_pnode_add_ctxt(pnode, lc); /* Initially zeroed by calloc above */
pnode->alloc_next = head;
head = pnode;
root = pnode;
++n_lc_alloc;
hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid);
lc_pnodelist =
glist_add_ptr(lc_pnodelist, (void *) pnode);
}
}
glist_free(lc_pnodelist);
}
else { /* Filler word; no context modelled */
ssid = bin_mdef_pid2ssid(lextree->mdef, ci); /* probably the same... */
tmatid = bin_mdef_pid2tmatid(lextree->mdef, ci);
pnode = (fsg_pnode_t *) ckd_calloc(1, sizeof(fsg_pnode_t));
pnode->ctx = lextree->ctx;
pnode->next.fsglink = fsglink;
pnode->logs2prob = (fsg_link_logs2prob(fsglink) >> SENSCR_SHIFT)
+ lextree->wip + lextree->pip;
pnode->ci_ext = silcipid; /* Presents SIL as context to neighbors */
pnode->ppos = 0;
pnode->leaf = TRUE;
pnode->sibling = root;
fsg_pnode_add_all_ctxt(&(pnode->ctxt));
pnode->alloc_next = head;
head = pnode;
root = pnode;
++n_int_alloc;
hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid);
}
}
else { /* Multi-phone word */
fsg_pnode_t **ssid_pnode_map; /* Temp array of ssid->pnode mapping */
ssid_pnode_map =
(fsg_pnode_t **) ckd_calloc(n_ci, sizeof(fsg_pnode_t *));
lc_pnodelist = NULL;
rc_pnodelist = NULL;
for (p = 0; p < pronlen; p++) {
int ci = dict_pron(lextree->dict, dictwid, p);
if (p == 0) { /* Root phone, handle required left contexts */
/* Find if we already have an lc_pnodelist for the first phone of this word */
fsg_glist_linklist_t *glist;
rc = dict_pron(lextree->dict, dictwid, 1);
for (glist = *curglist;
glist && glist->glist;
glist = glist->next) {
if (glist->ci == ci && glist->rc == rc)
break;
}
if (glist && glist->glist) {
assert(glist->ci == ci && glist->rc == rc);
/* We've found a valid glist. Hook to it and move to next phoneme */
E_DEBUG(2,("Found match for (%d,%d)\n", ci, rc));
lc_pnodelist = glist->glist;
/* Set the predecessor node for the future tree first */
pred = (fsg_pnode_t *) gnode_ptr(lc_pnodelist);
continue;
}
else {
/* Two cases that can bring us here
* a. glist == NULL, i.e. end of current list. Create new entry.
* b. glist->glist == NULL, i.e. first entry into list.
*/
if (glist == NULL) { /* Case a; reduce it to case b by allocing glist */
glist = (fsg_glist_linklist_t*) ckd_calloc(1, sizeof(fsg_glist_linklist_t));
glist->next = *curglist;
*curglist = glist;
}
glist->ci = ci;
glist->rc = rc;
lc_pnodelist = glist->glist = NULL; /* Gets created below */
}
for (i = 0; lclist[i] >= 0; i++) {
lc = lclist[i];
ssid = dict2pid_ldiph_lc(lextree->d2p, ci, rc, lc);
tmatid = bin_mdef_pid2tmatid(lextree->mdef, dict_first_phone(lextree->dict, dictwid));
/* Compression is not done by d2p, so we do it
* here. This might be slow, but it might not
* be... we'll see. */
pnode = ssid_pnode_map[0];
for (j = 0; j < n_ci && ssid_pnode_map[j] != NULL; ++j) {
pnode = ssid_pnode_map[j];
if (hmm_nonmpx_ssid(&pnode->hmm) == ssid)
break;
}
assert(j < n_ci);
if (!pnode) { /* Allocate pnode for this new ssid */
pnode =
(fsg_pnode_t *) ckd_calloc(1,
sizeof
(fsg_pnode_t));
pnode->ctx = lextree->ctx;
/* This bit is tricky! For now we'll put the prob in the final link only */
/* pnode->logs2prob = (fsg_link_logs2prob(fsglink) >> SENSCR_SHIFT)
+ lextree->wip + lextree->pip; */
pnode->logs2prob = lextree->wip + lextree->pip;
pnode->ci_ext = dict_first_phone(lextree->dict, dictwid);
pnode->ppos = 0;
pnode->leaf = FALSE;
pnode->sibling = root; /* All root nodes linked together */
pnode->alloc_next = head;
head = pnode;
root = pnode;
++n_lc_alloc;
hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid);
lc_pnodelist =
glist_add_ptr(lc_pnodelist, (void *) pnode);
ssid_pnode_map[j] = pnode;
}
fsg_pnode_add_ctxt(pnode, lc);
}
/* Put the lc_pnodelist back into glist */
glist->glist = lc_pnodelist;
/* The predecessor node for the future tree is the root */
pred = root;
}
else if (p != pronlen - 1) { /* Word internal phone */
fsg_pnode_t *pnodeyoungest;
ssid = dict2pid_internal(lextree->d2p, dictwid, p);
tmatid = bin_mdef_pid2tmatid(lextree->mdef, dict_pron (lextree->dict, dictwid, p));
/* First check if we already have this ssid in our tree */
pnode = pred->next.succ;
pnodeyoungest = pnode; /* The youngest sibling */
while (pnode && (hmm_nonmpx_ssid(&pnode->hmm) != ssid || pnode->leaf)) {
pnode = pnode->sibling;
}
if (pnode && (hmm_nonmpx_ssid(&pnode->hmm) == ssid && !pnode->leaf)) {
/* Found the ssid; go to next phoneme */
E_DEBUG(2,("Found match for %d\n", ci));
pred = pnode;
continue;
}
/* pnode not found, allocate it */
pnode = (fsg_pnode_t *) ckd_calloc(1, sizeof(fsg_pnode_t));
pnode->ctx = lextree->ctx;
pnode->logs2prob = lextree->pip;
pnode->ci_ext = dict_pron(lextree->dict, dictwid, p);
pnode->ppos = p;
pnode->leaf = FALSE;
pnode->sibling = pnodeyoungest; /* May be NULL */
if (p == 1) { /* Predecessor = set of root nodes for left ctxts */
for (gn = lc_pnodelist; gn; gn = gnode_next(gn)) {
pred = (fsg_pnode_t *) gnode_ptr(gn);
pred->next.succ = pnode;
}
}
else { /* Predecessor = word internal node */
pred->next.succ = pnode;
}
pnode->alloc_next = head;
head = pnode;
++n_int_alloc;
hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid);
pred = pnode;
}
else { /* Leaf phone, handle required right contexts */
/* Note, leaf phones are not part of the tree */
xwdssid_t *rssid;
memset((void *) ssid_pnode_map, 0,
n_ci * sizeof(fsg_pnode_t *));
lc = dict_pron(lextree->dict, dictwid, p-1);
rssid = dict2pid_rssid(lextree->d2p, ci, lc);
tmatid = bin_mdef_pid2tmatid(lextree->mdef, dict_pron (lextree->dict, dictwid, p));
for (i = 0; rclist[i] >= 0; i++) {
rc = rclist[i];
j = rssid->cimap[rc];
ssid = rssid->ssid[j];
pnode = ssid_pnode_map[j];
if (!pnode) { /* Allocate pnode for this new ssid */
pnode =
(fsg_pnode_t *) ckd_calloc(1,
sizeof
(fsg_pnode_t));
pnode->ctx = lextree->ctx;
/* We are plugging the word prob here. Ugly */
/* pnode->logs2prob = lextree->pip; */
pnode->logs2prob = (fsg_link_logs2prob(fsglink) >> SENSCR_SHIFT)
+ lextree->pip;
pnode->ci_ext = dict_pron(lextree->dict, dictwid, p);
pnode->ppos = p;
pnode->leaf = TRUE;
pnode->sibling = rc_pnodelist ?
(fsg_pnode_t *) gnode_ptr(rc_pnodelist) : NULL;
pnode->next.fsglink = fsglink;
pnode->alloc_next = head;
head = pnode;
++n_rc_alloc;
hmm_init(lextree->ctx, &pnode->hmm, FALSE, ssid, tmatid);
rc_pnodelist =
glist_add_ptr(rc_pnodelist, (void *) pnode);
ssid_pnode_map[j] = pnode;
}
else {
assert(hmm_nonmpx_ssid(&pnode->hmm) == ssid);
}
fsg_pnode_add_ctxt(pnode, rc);
}
if (p == 1) { /* Predecessor = set of root nodes for left ctxts */
for (gn = lc_pnodelist; gn; gn = gnode_next(gn)) {
pred = (fsg_pnode_t *) gnode_ptr(gn);
if (!pred->next.succ)
pred->next.succ = (fsg_pnode_t *) gnode_ptr(rc_pnodelist);
else {
/* Link to the end of the sibling chain */
fsg_pnode_t *succ = pred->next.succ;
while (succ->sibling) succ = succ->sibling;
succ->sibling = (fsg_pnode_t*) gnode_ptr(rc_pnodelist);
/* Since all entries of lc_pnodelist point
to the same array, sufficient to update it once */
break;
}
}
}
else { /* Predecessor = word internal node */
if (!pred->next.succ)
pred->next.succ = (fsg_pnode_t *) gnode_ptr(rc_pnodelist);
else {
/* Link to the end of the sibling chain */
fsg_pnode_t *succ = pred->next.succ;
while (succ->sibling) succ = succ->sibling;
succ->sibling = (fsg_pnode_t *) gnode_ptr(rc_pnodelist);
}
}
}
}
ckd_free((void *) ssid_pnode_map);
/* glist_free(lc_pnodelist); Nope; this gets freed outside */
glist_free(rc_pnodelist);
}
E_DEBUG(2,("Allocated %d HMMs (%d lc, %d rc, %d internal)\n",
n_lc_alloc + n_rc_alloc + n_int_alloc,
n_lc_alloc, n_rc_alloc, n_int_alloc));
*alloc_head = head;
return root;
}
static fsg_pnode_t *
fsg_psubtree_init(fsg_lextree_t *lextree,
fsg_model_t * fsg, int32 from_state,
fsg_pnode_t ** alloc_head)
{
fsg_arciter_t *itor;
fsg_link_t *fsglink;
fsg_pnode_t *root;
int32 n_ci, n_arc;
fsg_glist_linklist_t *glist = NULL;
root = NULL;
assert(*alloc_head == NULL);
n_ci = bin_mdef_n_ciphone(lextree->mdef);
if (n_ci > (FSG_PNODE_CTXT_BVSZ * 32)) {
E_FATAL
("#phones > %d; increase FSG_PNODE_CTXT_BVSZ and recompile\n",
FSG_PNODE_CTXT_BVSZ * 32);
}
n_arc = 0;
for (itor = fsg_model_arcs(fsg, from_state); itor;
itor = fsg_arciter_next(itor)) {
int32 dst;
fsglink = fsg_arciter_get(itor);
dst = fsglink->to_state;
if (fsg_link_wid(fsglink) < 0)
continue;
E_DEBUG(2,("Building lextree for arc from %d to %d: %s\n",
from_state, dst, fsg_model_word_str(fsg, fsg_link_wid(fsglink))));
root = psubtree_add_trans(lextree, root, &glist, fsglink,
lextree->lc[from_state],
lextree->rc[dst],
alloc_head);
++n_arc;
}
E_DEBUG(2,("State %d has %d outgoing arcs\n", from_state, n_arc));
fsg_glist_linklist_free(glist);
return root;
}
static void
fsg_psubtree_free(fsg_pnode_t * head)
{
fsg_pnode_t *next;
while (head) {
next = head->alloc_next;
hmm_deinit(&head->hmm);
ckd_free(head);
head = next;
}
}
void fsg_psubtree_dump_node(fsg_lextree_t *tree, fsg_pnode_t *node, FILE *fp)
{
int32 i;
fsg_link_t *tl;
/* Indentation */
for (i = 0; i <= node->ppos; i++)
fprintf(fp, " ");
fprintf(fp, "%p.@", node); /* Pointer used as node
* ID */
fprintf(fp, " %5d.SS", hmm_nonmpx_ssid(&node->hmm));
fprintf(fp, " %10d.LP", node->logs2prob);
fprintf(fp, " %p.SIB", node->sibling);
fprintf(fp, " %s.%d", bin_mdef_ciphone_str(tree->mdef, node->ci_ext), node->ppos);
if ((node->ppos == 0) || node->leaf) {
fprintf(fp, " [");
for (i = 0; i < FSG_PNODE_CTXT_BVSZ; i++)
fprintf(fp, "%08x", node->ctxt.bv[i]);
fprintf(fp, "]");
}
if (node->leaf) {
tl = node->next.fsglink;
fprintf(fp, " {%s[%d->%d](%d)}",
fsg_model_word_str(tree->fsg, tl->wid),
tl->from_state, tl->to_state, tl->logs2prob);
} else {
fprintf(fp, " %p.NXT", node->next.succ);
}
fprintf(fp, "\n");
return;
}
void
fsg_psubtree_dump(fsg_lextree_t *tree, fsg_pnode_t *root, FILE * fp)
{
fsg_pnode_t *succ;
if (root == NULL) return;
if (root->ppos == 0) {
while(root->sibling && root->sibling->next.succ == root->next.succ) {
fsg_psubtree_dump_node(tree, root, fp);
root = root->sibling;
}
fflush(fp);
}
fsg_psubtree_dump_node(tree, root, fp);
if (root->leaf) {
if (root->ppos == 0 && root->sibling) { /* For single-phone words */
fsg_psubtree_dump(tree, root->sibling,fp);
}
return;
}
succ = root->next.succ;
while(succ) {
fsg_psubtree_dump(tree, succ,fp);
succ = succ->sibling;
}
if (root->ppos == 0) {
fsg_psubtree_dump(tree, root->sibling,fp);
fflush(fp);
}
return;
}
void
fsg_psubtree_pnode_deactivate(fsg_pnode_t * pnode)
{
hmm_clear(&pnode->hmm);
}
fsg_lextree_s
Collection of lextrees for an FSG.
Definition: fsg_lextree.h:180
SENSCR_SHIFT
#define SENSCR_SHIFT
Shift count for senone scores.
Definition: hmm.h:73
fsg_lextree_s::d2p
dict2pid_t * d2p
Context-dependent phone mappings for this FSG.
Definition: fsg_lextree.h:184
fsg_lextree_s::mdef
bin_mdef_t * mdef
Model definition (triphone mappings).
Definition: fsg_lextree.h:185
xwdssid_t::cimap
s3cipid_t * cimap
Index into ssid[] above for each ci phone.
Definition: dict2pid.h:75
bin_mdef_s
Definition: bin_mdef.h:117
bin_mdef_ciphone_str
const char * bin_mdef_ciphone_str(bin_mdef_t *m, int32 ci)
In: ciphone id for which name wanted.
Definition: bin_mdef.c:736
xwdssid_t::ssid
s3ssid_t * ssid
Senone Sequence ID list for all context ciphones.
Definition: dict2pid.h:74
fsg_pnode_ctxt_t
Definition: fsg_lextree.h:60
dict2pid_internal
s3ssid_t dict2pid_internal(dict2pid_t *d2p, int32 wid, int pos)
Return the senone sequence ID for the given word position.
Definition: dict2pid.c:367
fsg_lextree_free
void fsg_lextree_free(fsg_lextree_t *lextree)
Free lextrees for an FSG.
Definition: fsg_lextree.c:286
fsg_lextree_s::ctx
hmm_context_t * ctx
HMM context structure.
Definition: fsg_lextree.h:182
hmm_clear
void hmm_clear(hmm_t *h)
Reset the states of the HMM to the invalid condition.
Definition: hmm.c:183
hmm_context_t
Shared information between a set of HMMs.
dict2pid_rssid
#define dict2pid_rssid(d, ci, lc)
Access macros; not designed for arbitrary use.
Definition: dict2pid.h:115
fsg_pnode_add_all_ctxt
void fsg_pnode_add_all_ctxt(fsg_pnode_ctxt_t *ctxt)
Set all flags on in the given context bitvector.
Definition: fsg_lextree.c:327
hmm_deinit
void hmm_deinit(hmm_t *hmm)
Free an HMM structure, releasing internal data (but not the HMM structure itself).
Definition: hmm.c:111
dict2pid_t
Building composite triphone (as well as word internal triphones) with the dictionary.
Definition: dict2pid.h:84
dict_filler_word
int dict_filler_word(dict_t *d, s3wid_t w)
Return 1 if w is a filler word, 0 if not.
Definition: dict.c:413
hmm_init
void hmm_init(hmm_context_t *ctx, hmm_t *hmm, int mpx, int ssid, int tmatid)
Populate a previously-allocated HMM structure, allocating internal data.
Definition: hmm.c:89
fsg_psubtree_pnode_deactivate
void fsg_psubtree_pnode_deactivate(fsg_pnode_t *pnode)
Mark the given pnode as inactive (for search).
Definition: fsg_lextree.c:831
fsg_lextree_s::dict
dict_t * dict
Pronunciation dictionary for this FSG.
Definition: fsg_lextree.h:183
fsg_lextree_dump
void fsg_lextree_dump(fsg_lextree_t *lextree, FILE *fp)
Print an FSG lextree to a file for debugging.
Definition: fsg_lextree.c:273
fsg_lextree_s::lc
int16 ** lc
Left context triphone mappings for FSG.
Definition: fsg_lextree.h:204
fsg_lextree_init
fsg_lextree_t * fsg_lextree_init(fsg_model_t *fsg, dict_t *dict, dict2pid_t *d2p, bin_mdef_t *mdef, hmm_context_t *ctx, int32 wip, int32 pip)
Create, initialize, and return a new phonetic lextree for the given FSG.
Definition: fsg_lextree.c:215
xwdssid_t
cross word triphone model structure
Definition: dict2pid.h:73
fsg_lextree_s::fsg
fsg_model_t * fsg
The fsg for which this lextree is built.
Definition: fsg_lextree.h:181
fsg_lextree_s::rc
int16 ** rc
Right context triphone mappings for FSG.
Definition: fsg_lextree.h:205
dict_pron
#define dict_pron(d, w, p)
The CI phones of the word w at position p.
Definition: dict.h:165
dict_t
a structure for a dictionary.
Definition: dict.h:76
dict_wordid
POCKETSPHINX_EXPORT s3wid_t dict_wordid(dict_t *d, const char *word)
Return word id for given word string if present.
Definition: dict.c:399
fsg_pnode_s
Definition: fsg_lextree.h:79
fsg_glist_linklist_t
Definition: fsg_lextree.c:56
fsg_pnode_ctxt_sub_generic
uint32 fsg_pnode_ctxt_sub_generic(fsg_pnode_ctxt_t *src, fsg_pnode_ctxt_t *sub)
Generic variant for arbitrary size.
Definition: fsg_lextree.c:335