LatticeIncrementalDecoderTpl< FST, Token > Class Template Reference

This is an extention to the "normal" lattice-generating decoder. More...

#include <lattice-incremental-decoder.h>

Collaboration diagram for LatticeIncrementalDecoderTpl< FST, Token >:

Classes

struct  TokenList
 

Public Types

using Arc = typename FST::Arc
 
using Label = typename Arc::Label
 
using StateId = typename Arc::StateId
 
using Weight = typename Arc::Weight
 
using ForwardLinkT = decoder::ForwardLink< Token >
 

Public Member Functions

 LatticeIncrementalDecoderTpl (const FST &fst, const TransitionModel &trans_model, const LatticeIncrementalDecoderConfig &config)
 
 LatticeIncrementalDecoderTpl (const LatticeIncrementalDecoderConfig &config, FST *fst, const TransitionModel &trans_model)
 
void SetOptions (const LatticeIncrementalDecoderConfig &config)
 
const LatticeIncrementalDecoderConfigGetOptions () const
 
 ~LatticeIncrementalDecoderTpl ()
 
bool Decode (DecodableInterface *decodable)
 CAUTION: it's unlikely that you will ever want to call this function. More...
 
bool ReachedFinal () const
 says whether a final-state was active on the last frame. More...
 
const CompactLatticeGetLattice (int32 num_frames_to_include, bool use_final_probs=false)
 This decoder has no GetBestPath() function. More...
 
int NumFramesInLattice () const
 
void InitDecoding ()
 InitDecoding initializes the decoding, and should only be used if you intend to call AdvanceDecoding(). More...
 
void AdvanceDecoding (DecodableInterface *decodable, int32 max_num_frames=-1)
 This will decode until there are no more frames ready in the decodable object. More...
 
BaseFloat FinalRelativeCost () const
 FinalRelativeCost() serves the same purpose as ReachedFinal(), but gives more information. More...
 
int32 NumFramesDecoded () const
 Returns the number of frames decoded so far. More...
 
void FinalizeDecoding ()
 Finalizes the decoding, doing an extra pruning step on the last frame that uses the final-probs. More...
 

Protected Types

using Elem = typename HashList< StateId, Token * >::Elem
 

Protected Member Functions

void PossiblyResizeHash (size_t num_toks)
 
Token * FindOrAddToken (StateId state, int32 frame_plus_one, BaseFloat tot_cost, Token *backpointer, bool *changed)
 
void PruneForwardLinks (int32 frame_plus_one, bool *extra_costs_changed, bool *links_pruned, BaseFloat delta)
 
void ComputeFinalCosts (unordered_map< Token *, BaseFloat > *final_costs, BaseFloat *final_relative_cost, BaseFloat *final_best_cost) const
 
void PruneForwardLinksFinal ()
 
void PruneTokensForFrame (int32 frame_plus_one)
 
void PruneActiveTokens (BaseFloat delta)
 
BaseFloat GetCutoff (Elem *list_head, size_t *tok_count, BaseFloat *adaptive_beam, Elem **best_elem)
 Gets the weight cutoff. Also counts the active tokens. More...
 
BaseFloat ProcessEmitting (DecodableInterface *decodable)
 
void ProcessNonemitting (BaseFloat cost_cutoff)
 
Label AllocateNewTokenLabel ()
 
void DeleteElems (Elem *list)
 
void ClearActiveTokens ()
 
int32 GetNumToksForFrame (int32 frame)
 
void UpdateLatticeDeterminization ()
 UpdateLatticeDeterminization() ensures the work of determinization is kept up to date so that when you do need the lattice you can get it fast. More...
 
 KALDI_DISALLOW_COPY_AND_ASSIGN (LatticeIncrementalDecoderTpl)
 

Static Protected Member Functions

static void DeleteForwardLinks (Token *tok)
 NOTE: for parts the internal implementation that are shared with LatticeFasterDecoer, we have removed the comments. More...
 

Protected Attributes

HashList< StateId, Token * > toks_
 
std::vector< TokenListactive_toks_
 
std::vector< StateIdqueue_
 
std::vector< BaseFloattmp_array_
 
const FST * fst_
 
bool delete_fst_
 
std::vector< BaseFloatcost_offsets_
 
int32 num_toks_
 
bool warned_
 
bool decoding_finalized_
 
unordered_map< Token *, BaseFloatfinal_costs_
 
BaseFloat final_relative_cost_
 
BaseFloat final_best_cost_
 
LatticeIncrementalDecoderConfig config_
 
LatticeIncrementalDeterminizer determinizer_
 Much of the the incremental determinization algorithm is encapsulated in the determinize_ object. More...
 
unordered_map< Token *, StateIdtemp_token_map_
 
int32 num_frames_in_lattice_
 num_frames_in_lattice_ is the highest `num_frames_to_include_` argument for any prior call to GetLattice(). More...
 
unordered_map< Token *, Labeltoken2label_map_
 
unordered_map< Token *, Labeltoken2label_map_temp_
 
Label next_token_label_
 

Detailed Description

template<typename FST, typename Token = decoder::StdToken>
class kaldi::LatticeIncrementalDecoderTpl< FST, Token >

This is an extention to the "normal" lattice-generating decoder.

See Lattice generation FasterDecoder: a more optimized decoder and SimpleDecoder: the simplest possible decoder for more information.

The main difference is the incremental determinization which will be discussed in the function GetLattice(). This means that the work of determinizatin isn't done all at once at the end of the file, but incrementally while decoding. See the comment at the top of this file for more explanation.

The decoder is templated on the FST type and the token type. The token type will normally be StdToken, but also may be BackpointerToken which is to support quick lookup of the current best path (see lattice-faster-online-decoder.h)

The FST you invoke this decoder with is expected to be of type Fst::Fst<fst::StdArc>, a.k.a. StdFst, or GrammarFst. If you invoke it with FST == StdFst and it notices that the actual FST type is fst::VectorFst<fst::StdArc> or fst::ConstFst<fst::StdArc>, the decoder object will internally cast itself to one that is templated on those more specific types; this is an optimization for speed.

Definition at line 465 of file lattice-incremental-decoder.h.

Member Typedef Documentation

◆ Arc

using Arc = typename FST::Arc

Definition at line 467 of file lattice-incremental-decoder.h.

◆ Elem

using Elem = typename HashList<StateId, Token *>::Elem
protected

Definition at line 625 of file lattice-incremental-decoder.h.

◆ ForwardLinkT

Definition at line 471 of file lattice-incremental-decoder.h.

◆ Label

using Label = typename Arc::Label

Definition at line 468 of file lattice-incremental-decoder.h.

◆ StateId

using StateId = typename Arc::StateId

Definition at line 469 of file lattice-incremental-decoder.h.

◆ Weight

using Weight = typename Arc::Weight

Definition at line 470 of file lattice-incremental-decoder.h.

Constructor & Destructor Documentation

◆ LatticeIncrementalDecoderTpl() [1/2]

LatticeIncrementalDecoderTpl ( const FST &  fst,
const TransitionModel trans_model,
const LatticeIncrementalDecoderConfig config 
)

Definition at line 28 of file lattice-incremental-decoder.cc.

31  : fst_(&fst),
32  delete_fst_(false),
33  num_toks_(0),
34  config_(config),
35  determinizer_(trans_model, config) {
36  config.Check();
37  toks_.SetSize(1000); // just so on the first frame we do something reasonable.
38 }
For an extended explanation of the framework of which grammar-fsts are a part, please see Support for...
Definition: graph.dox:21
void SetSize(size_t sz)
SetSize tells the object how many hash buckets to allocate (should typically be at least twice the nu...
Definition: hash-list-inl.h:37
LatticeIncrementalDeterminizer determinizer_
Much of the the incremental determinization algorithm is encapsulated in the determinize_ object...

◆ LatticeIncrementalDecoderTpl() [2/2]

LatticeIncrementalDecoderTpl ( const LatticeIncrementalDecoderConfig config,
FST *  fst,
const TransitionModel trans_model 
)

Definition at line 41 of file lattice-incremental-decoder.cc.

44  : fst_(fst),
45  delete_fst_(true),
46  num_toks_(0),
47  config_(config),
48  determinizer_(trans_model, config) {
49  config.Check();
50  toks_.SetSize(1000); // just so on the first frame we do something reasonable.
51 }
For an extended explanation of the framework of which grammar-fsts are a part, please see Support for...
Definition: graph.dox:21
void SetSize(size_t sz)
SetSize tells the object how many hash buckets to allocate (should typically be at least twice the nu...
Definition: hash-list-inl.h:37
LatticeIncrementalDeterminizer determinizer_
Much of the the incremental determinization algorithm is encapsulated in the determinize_ object...

◆ ~LatticeIncrementalDecoderTpl()

Definition at line 54 of file lattice-incremental-decoder.cc.

54  {
57  if (delete_fst_) delete fst_;
58 }
Elem * Clear()
Clears the hash and gives the head of the current list to the user; ownership is transferred to the u...
Definition: hash-list-inl.h:46

Member Function Documentation

◆ AdvanceDecoding()

void AdvanceDecoding ( DecodableInterface decodable,
int32  max_num_frames = -1 
)

This will decode until there are no more frames ready in the decodable object.

You can keep calling it each time more frames become available (this is the normal pattern in a real-time/online decoding scenario). If max_num_frames is specified, it specifies the maximum number of frames the function will decode before returning.

Definition at line 539 of file lattice-incremental-decoder.cc.

Referenced by LatticeIncrementalDecoderTpl< FST, decoder::BackpointerToken >::AdvanceDecoding().

540  {
541  if (std::is_same<FST, fst::Fst<fst::StdArc> >::value) {
542  // if the type 'FST' is the FST base-class, then see if the FST type of fst_
543  // is actually VectorFst or ConstFst. If so, call the AdvanceDecoding()
544  // function after casting *this to the more specific type.
545  if (fst_->Type() == "const") {
546  LatticeIncrementalDecoderTpl<fst::ConstFst<fst::StdArc>, Token> *this_cast =
547  reinterpret_cast<
548  LatticeIncrementalDecoderTpl<fst::ConstFst<fst::StdArc>, Token> *>(
549  this);
550  this_cast->AdvanceDecoding(decodable, max_num_frames);
551  return;
552  } else if (fst_->Type() == "vector") {
553  LatticeIncrementalDecoderTpl<fst::VectorFst<fst::StdArc>, Token> *this_cast =
554  reinterpret_cast<
555  LatticeIncrementalDecoderTpl<fst::VectorFst<fst::StdArc>, Token> *>(
556  this);
557  this_cast->AdvanceDecoding(decodable, max_num_frames);
558  return;
559  }
560  }
561 
563  "You must call InitDecoding() before AdvanceDecoding");
564  int32 num_frames_ready = decodable->NumFramesReady();
565  // num_frames_ready must be >= num_frames_decoded, or else
566  // the number of frames ready must have decreased (which doesn't
567  // make sense) or the decodable object changed between calls
568  // (which isn't allowed).
569  KALDI_ASSERT(num_frames_ready >= NumFramesDecoded());
570  int32 target_frames_decoded = num_frames_ready;
571  if (max_num_frames >= 0)
572  target_frames_decoded =
573  std::min(target_frames_decoded, NumFramesDecoded() + max_num_frames);
574  while (NumFramesDecoded() < target_frames_decoded) {
575  if (NumFramesDecoded() % config_.prune_interval == 0) {
577  }
578  BaseFloat cost_cutoff = ProcessEmitting(decodable);
579  ProcessNonemitting(cost_cutoff);
580  }
582 }
void UpdateLatticeDeterminization()
UpdateLatticeDeterminization() ensures the work of determinization is kept up to date so that when yo...
kaldi::int32 int32
float BaseFloat
Definition: kaldi-types.h:29
int32 NumFramesDecoded() const
Returns the number of frames decoded so far.
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
BaseFloat ProcessEmitting(DecodableInterface *decodable)

◆ AllocateNewTokenLabel()

Label AllocateNewTokenLabel ( )
inlineprotected

◆ ClearActiveTokens()

void ClearActiveTokens ( )
protected

Definition at line 852 of file lattice-incremental-decoder.cc.

852  { // a cleanup routine, at utt end/begin
853  for (size_t i = 0; i < active_toks_.size(); i++) {
854  // Delete all tokens alive on this frame, and any forward
855  // links they may have.
856  for (Token *tok = active_toks_[i].toks; tok != NULL;) {
857  DeleteForwardLinks(tok);
858  Token *next_tok = tok->next;
859  delete tok;
860  num_toks_--;
861  tok = next_tok;
862  }
863  }
864  active_toks_.clear();
865  KALDI_ASSERT(num_toks_ == 0);
866 }
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
static void DeleteForwardLinks(Token *tok)
NOTE: for parts the internal implementation that are shared with LatticeFasterDecoer, we have removed the comments.

◆ ComputeFinalCosts()

void ComputeFinalCosts ( unordered_map< Token *, BaseFloat > *  final_costs,
BaseFloat final_relative_cost,
BaseFloat final_best_cost 
) const
protected

Definition at line 492 of file lattice-incremental-decoder.cc.

494  {
495  if (decoding_finalized_) {
496  // If we finalized decoding, the list toks_ will no longer exist, so return
497  // something we already computed.
498  if (final_costs) *final_costs = final_costs_;
499  if (final_relative_cost) *final_relative_cost = final_relative_cost_;
500  if (final_best_cost) *final_best_cost = final_best_cost_;
501  return;
502  }
503  if (final_costs != NULL) final_costs->clear();
504  const Elem *final_toks = toks_.GetList();
505  BaseFloat infinity = std::numeric_limits<BaseFloat>::infinity();
506  BaseFloat best_cost = infinity, best_cost_with_final = infinity;
507 
508  while (final_toks != NULL) {
509  StateId state = final_toks->key;
510  Token *tok = final_toks->val;
511  const Elem *next = final_toks->tail;
512  BaseFloat final_cost = fst_->Final(state).Value();
513  BaseFloat cost = tok->tot_cost, cost_with_final = cost + final_cost;
514  best_cost = std::min(cost, best_cost);
515  best_cost_with_final = std::min(cost_with_final, best_cost_with_final);
516  if (final_costs != NULL && final_cost != infinity)
517  (*final_costs)[tok] = final_cost;
518  final_toks = next;
519  }
520  if (final_relative_cost != NULL) {
521  if (best_cost == infinity && best_cost_with_final == infinity) {
522  // Likely this will only happen if there are no tokens surviving.
523  // This seems the least bad way to handle it.
524  *final_relative_cost = infinity;
525  } else {
526  *final_relative_cost = best_cost_with_final - best_cost;
527  }
528  }
529  if (final_best_cost != NULL) {
530  if (best_cost_with_final != infinity) { // final-state exists.
531  *final_best_cost = best_cost_with_final;
532  } else { // no final-state exists.
533  *final_best_cost = best_cost;
534  }
535  }
536 }
fst::StdArc::StateId StateId
typename HashList< StateId, Token * >::Elem Elem
float BaseFloat
Definition: kaldi-types.h:29
const Elem * GetList() const
Gives the head of the current list to the user.
Definition: hash-list-inl.h:61
unordered_map< Token *, BaseFloat > final_costs_

◆ Decode()

bool Decode ( DecodableInterface decodable)

CAUTION: it's unlikely that you will ever want to call this function.

In a scenario where you have the entire file and just want to decode it, there is no point using this decoder.

An example of how to do decoding together with incremental determinization. It decodes until there are no more frames left in the "decodable" object.

In this example, config_.determinize_delay, config_.determinize_period and config_.determinize_max_active are used to determine the time to call GetLattice().

Users will probably want to use appropriate combinations of AdvanceDecoding() and GetLattice() to build their application; this just gives you some idea how.

The function returns true if any kind of traceback is available (not necessarily from a final state).

Definition at line 121 of file lattice-incremental-decoder.cc.

Referenced by kaldi::DecodeUtteranceLatticeIncremental().

121  {
122  InitDecoding();
123 
124  // We use 1-based indexing for frames in this decoder (if you view it in
125  // terms of features), but note that the decodable object uses zero-based
126  // numbering, which we have to correct for when we call it.
127 
128  while (!decodable->IsLastFrame(NumFramesDecoded() - 1)) {
129  if (NumFramesDecoded() % config_.prune_interval == 0) {
131  }
133 
134  BaseFloat cost_cutoff = ProcessEmitting(decodable);
135  ProcessNonemitting(cost_cutoff);
136  }
137  Timer timer;
139  bool use_final_probs = true;
140  GetLattice(NumFramesDecoded(), use_final_probs);
141  KALDI_VLOG(2) << "Delay time during and after FinalizeDecoding()"
142  << "(secs): " << timer.Elapsed();
143 
144  // Returns true if we have any kind of traceback available (not necessarily
145  // to the end state; query ReachedFinal() for that).
146  return !active_toks_.empty() && active_toks_.back().toks != NULL;
147 }
void UpdateLatticeDeterminization()
UpdateLatticeDeterminization() ensures the work of determinization is kept up to date so that when yo...
void InitDecoding()
InitDecoding initializes the decoding, and should only be used if you intend to call AdvanceDecoding(...
float BaseFloat
Definition: kaldi-types.h:29
void FinalizeDecoding()
Finalizes the decoding, doing an extra pruning step on the last frame that uses the final-probs...
int32 NumFramesDecoded() const
Returns the number of frames decoded so far.
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156
BaseFloat ProcessEmitting(DecodableInterface *decodable)
const CompactLattice & GetLattice(int32 num_frames_to_include, bool use_final_probs=false)
This decoder has no GetBestPath() function.

◆ DeleteElems()

void DeleteElems ( Elem list)
protected

Definition at line 843 of file lattice-incremental-decoder.cc.

843  {
844  for (Elem *e = list, *e_tail; e != NULL; e = e_tail) {
845  e_tail = e->tail;
846  toks_.Delete(e);
847  }
848 }
typename HashList< StateId, Token * >::Elem Elem
void Delete(Elem *e)
Think of this like delete().
Definition: hash-list-inl.h:66

◆ DeleteForwardLinks()

void DeleteForwardLinks ( Token *  tok)
inlinestaticprotected

NOTE: for parts the internal implementation that are shared with LatticeFasterDecoer, we have removed the comments.

Definition at line 765 of file lattice-incremental-decoder.cc.

765  {
766  ForwardLinkT *l = tok->links, *m;
767  while (l != NULL) {
768  m = l->next;
769  delete l;
770  l = m;
771  }
772  tok->links = NULL;
773 }

◆ FinalizeDecoding()

void FinalizeDecoding ( )

Finalizes the decoding, doing an extra pruning step on the last frame that uses the final-probs.

May be called only once.

Definition at line 588 of file lattice-incremental-decoder.cc.

588  {
589  int32 final_frame_plus_one = NumFramesDecoded();
590  int32 num_toks_begin = num_toks_;
591  // PruneForwardLinksFinal() prunes the final frame (with final-probs), and
592  // sets decoding_finalized_.
594  for (int32 f = final_frame_plus_one - 1; f >= 0; f--) {
595  bool b1, b2; // values not used.
596  BaseFloat dontcare = 0.0; // delta of zero means we must always update
597  PruneForwardLinks(f, &b1, &b2, dontcare);
598  PruneTokensForFrame(f + 1);
599  }
601  KALDI_VLOG(4) << "pruned tokens from " << num_toks_begin << " to " << num_toks_;
602 }
kaldi::int32 int32
float BaseFloat
Definition: kaldi-types.h:29
void PruneForwardLinks(int32 frame_plus_one, bool *extra_costs_changed, bool *links_pruned, BaseFloat delta)
int32 NumFramesDecoded() const
Returns the number of frames decoded so far.
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156

◆ FinalRelativeCost()

BaseFloat FinalRelativeCost ( ) const

FinalRelativeCost() serves the same purpose as ReachedFinal(), but gives more information.

It returns the difference between the best (final-cost plus cost) of any token on the final frame, and the best cost of any token on the final frame. If it is infinity it means no final-states were present on the final frame. It will usually be nonnegative. If it not too positive (e.g. < 5 is my first guess, but this is not tested) you can take it as a good indication that we reached the final-state with reasonable likelihood.

Definition at line 409 of file lattice-incremental-decoder.cc.

409  {
410  BaseFloat relative_cost;
411  ComputeFinalCosts(NULL, &relative_cost, NULL);
412  return relative_cost;
413 }
float BaseFloat
Definition: kaldi-types.h:29
void ComputeFinalCosts(unordered_map< Token *, BaseFloat > *final_costs, BaseFloat *final_relative_cost, BaseFloat *final_best_cost) const

◆ FindOrAddToken()

Token * FindOrAddToken ( StateId  state,
int32  frame_plus_one,
BaseFloat  tot_cost,
Token *  backpointer,
bool changed 
)
inlineprotected

Definition at line 197 of file lattice-incremental-decoder.cc.

199  {
200  // Returns the Token pointer. Sets "changed" (if non-NULL) to true
201  // if the token was newly created or the cost changed.
202  KALDI_ASSERT(frame_plus_one < active_toks_.size());
203  Token *&toks = active_toks_[frame_plus_one].toks;
204  Elem *e_found = toks_.Find(state);
205  if (e_found == NULL) { // no such token presently.
206  const BaseFloat extra_cost = 0.0;
207  // tokens on the currently final frame have zero extra_cost
208  // as any of them could end up
209  // on the winning path.
210  Token *new_tok = new Token(tot_cost, extra_cost, NULL, toks, backpointer);
211  // NULL: no forward links yet
212  toks = new_tok;
213  num_toks_++;
214  toks_.Insert(state, new_tok);
215  if (changed) *changed = true;
216  return new_tok;
217  } else {
218  Token *tok = e_found->val; // There is an existing Token for this state.
219  if (tok->tot_cost > tot_cost) { // replace old token
220  tok->tot_cost = tot_cost;
221  // SetBackpointer() just does tok->backpointer = backpointer in
222  // the case where Token == BackpointerToken, else nothing.
223  tok->SetBackpointer(backpointer);
224  // we don't allocate a new token, the old stays linked in active_toks_
225  // we only replace the tot_cost
226  // in the current frame, there are no forward links (and no extra_cost)
227  // only in ProcessNonemitting we have to delete forward links
228  // in case we visit a state for the second time
229  // those forward links, that lead to this replaced token before:
230  // they remain and will hopefully be pruned later (PruneForwardLinks...)
231  if (changed) *changed = true;
232  } else {
233  if (changed) *changed = false;
234  }
235  return tok;
236  }
237 }
Elem * Insert(I key, T val)
Insert inserts a new element into the hashtable/stored list.
typename HashList< StateId, Token * >::Elem Elem
float BaseFloat
Definition: kaldi-types.h:29
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
Elem * Find(I key)
Find tries to find this element in the current list using the hashtable.
Definition: hash-list-inl.h:72

◆ GetCutoff()

BaseFloat GetCutoff ( Elem list_head,
size_t tok_count,
BaseFloat adaptive_beam,
Elem **  best_elem 
)
protected

Gets the weight cutoff. Also counts the active tokens.

Definition at line 606 of file lattice-incremental-decoder.cc.

607  {
608  BaseFloat best_weight = std::numeric_limits<BaseFloat>::infinity();
609  // positive == high cost == bad.
610  size_t count = 0;
611  if (config_.max_active == std::numeric_limits<int32>::max() &&
612  config_.min_active == 0) {
613  for (Elem *e = list_head; e != NULL; e = e->tail, count++) {
614  BaseFloat w = static_cast<BaseFloat>(e->val->tot_cost);
615  if (w < best_weight) {
616  best_weight = w;
617  if (best_elem) *best_elem = e;
618  }
619  }
620  if (tok_count != NULL) *tok_count = count;
621  if (adaptive_beam != NULL) *adaptive_beam = config_.beam;
622  return best_weight + config_.beam;
623  } else {
624  tmp_array_.clear();
625  for (Elem *e = list_head; e != NULL; e = e->tail, count++) {
626  BaseFloat w = e->val->tot_cost;
627  tmp_array_.push_back(w);
628  if (w < best_weight) {
629  best_weight = w;
630  if (best_elem) *best_elem = e;
631  }
632  }
633  if (tok_count != NULL) *tok_count = count;
634 
635  BaseFloat beam_cutoff = best_weight + config_.beam,
636  min_active_cutoff = std::numeric_limits<BaseFloat>::infinity(),
637  max_active_cutoff = std::numeric_limits<BaseFloat>::infinity();
638 
639  KALDI_VLOG(6) << "Number of tokens active on frame " << NumFramesDecoded()
640  << " is " << tmp_array_.size();
641 
642  if (tmp_array_.size() > static_cast<size_t>(config_.max_active)) {
643  std::nth_element(tmp_array_.begin(), tmp_array_.begin() + config_.max_active,
644  tmp_array_.end());
645  max_active_cutoff = tmp_array_[config_.max_active];
646  }
647  if (max_active_cutoff < beam_cutoff) { // max_active is tighter than beam.
648  if (adaptive_beam)
649  *adaptive_beam = max_active_cutoff - best_weight + config_.beam_delta;
650  return max_active_cutoff;
651  }
652  if (tmp_array_.size() > static_cast<size_t>(config_.min_active)) {
653  if (config_.min_active == 0)
654  min_active_cutoff = best_weight;
655  else {
656  std::nth_element(tmp_array_.begin(), tmp_array_.begin() + config_.min_active,
657  tmp_array_.size() > static_cast<size_t>(config_.max_active)
658  ? tmp_array_.begin() + config_.max_active
659  : tmp_array_.end());
660  min_active_cutoff = tmp_array_[config_.min_active];
661  }
662  }
663  if (min_active_cutoff > beam_cutoff) { // min_active is looser than beam.
664  if (adaptive_beam)
665  *adaptive_beam = min_active_cutoff - best_weight + config_.beam_delta;
666  return min_active_cutoff;
667  } else {
668  *adaptive_beam = config_.beam;
669  return beam_cutoff;
670  }
671  }
672 }
typename HashList< StateId, Token * >::Elem Elem
const size_t count
float BaseFloat
Definition: kaldi-types.h:29
int32 NumFramesDecoded() const
Returns the number of frames decoded so far.
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156

◆ GetLattice()

const CompactLattice & GetLattice ( int32  num_frames_to_include,
bool  use_final_probs = false 
)

This decoder has no GetBestPath() function.

If you need that functionality you should probably use lattice-incremental-online-decoder.h, which makes it very efficient to obtain the best path. This GetLattice() function returns the lattice containing `num_frames_to_decode` frames; this will be all frames decoded so far, if you let num_frames_to_decode == NumFramesDecoded(), but it will generally be better to make it a few frames less than that to avoid the lattice having too many active states at the end.

Parameters
[in]num_frames_to_includeThe number of frames that you want to be included in the lattice. Must be >= NumFramesInLattice() and <= NumFramesDecoded().
[in]use_final_probsTrue if you want the final-probs of HCLG to be included in the output lattice. Must not be set to true if num_frames_to_include != NumFramesDecoded(). Must be set to true if you have previously called FinalizeDecoding().

(If no state was final on frame `num_frames_to_include`, the final-probs won't be included regardless of `use_final_probs`; you can test whether this was the case by calling ReachedFinal().

Returns
clat The CompactLattice representing what has been decoded up until `num_frames_to_include` (e.g., LatticeStateTimes() on this lattice would return `num_frames_to_include`).

See also UpdateLatticeDeterminizaton(). Caution: this const ref is only valid until the next time you call AdvanceDecoding() or GetLattice().

CAUTION: the lattice may contain disconnnected states; you should call Connect() on the output before writing it out.

Definition at line 870 of file lattice-incremental-decoder.cc.

Referenced by kaldi::DecodeUtteranceLatticeIncremental().

872  {
873  KALDI_ASSERT(num_frames_to_include >= num_frames_in_lattice_ &&
874  num_frames_to_include <= NumFramesDecoded());
875 
876 
877  if (num_frames_in_lattice_ > 0 &&
878  determinizer_.GetLattice().NumStates() == 0) {
879  /* Something went wrong, lattice is empty and will continue to be empty.
880  User-level code should detect and deal with this.
881  */
882  num_frames_in_lattice_ = num_frames_to_include;
883  return determinizer_.GetLattice();
884  }
885 
886  if (decoding_finalized_ && !use_final_probs) {
887  // This is not supported
888  KALDI_ERR << "You cannot get the lattice without final-probs after "
889  "calling FinalizeDecoding().";
890  }
891  if (use_final_probs && num_frames_to_include != NumFramesDecoded()) {
892  /* This is because we only remember the relation between HCLG states and
893  Tokens for the current frame; the Token does not have a `state` field. */
894  KALDI_ERR << "use-final-probs may no be true if you are not "
895  "getting a lattice for all frames decoded so far.";
896  }
897 
898 
899  if (num_frames_to_include > num_frames_in_lattice_) {
900  /* Make sure the token-pruning is up to date. If we just pruned the tokens,
901  this will do very little work. */
903 
904  if (determinizer_.GetLattice().NumStates() == 0 ||
908  }
909 
910  Lattice chunk_lat;
911 
912  unordered_map<Label, LatticeArc::StateId> token_label2state;
913  if (num_frames_in_lattice_ != 0) {
915  &token_label2state);
916  }
917 
918  // tok_map will map from Token* to state-id in chunk_lat.
919  // The cur and prev versions alternate on different frames.
920  unordered_map<Token*, StateId> &tok2state_map(temp_token_map_);
921  tok2state_map.clear();
922 
923  unordered_map<Token*, Label> &next_token2label_map(token2label_map_temp_);
924  next_token2label_map.clear();
925 
926  { // Deal with the last frame in the chunk, the one numbered `num_frames_to_include`.
927  // (Yes, this is backwards). We allocate token labels, and set tokens as
928  // final, but don't add any transitions. This may leave some states
929  // disconnected (e.g. due to chains of nonemitting arcs), but it's OK; we'll
930  // fix it when we generate the next chunk of lattice.
931  int32 frame = num_frames_to_include;
932  // Allocate state-ids for all tokens on this frame.
933 
934  for (Token *tok = active_toks_[frame].toks; tok != NULL; tok = tok->next) {
935  /* If we included the final-costs at this stage, they will cause
936  non-final states to be pruned out from the end of the lattice. */
937  BaseFloat final_cost;
938  { // This block computes final_cost
939  if (decoding_finalized_) {
940  if (final_costs_.empty()) {
941  final_cost = 0.0; /* No final-state survived, so treat all as final
942  * with probability One(). */
943  } else {
944  auto iter = final_costs_.find(tok);
945  if (iter == final_costs_.end())
946  final_cost = std::numeric_limits<BaseFloat>::infinity();
947  else
948  final_cost = iter->second;
949  }
950  } else {
951  /* this is a `fake` final-cost used to guide pruning. It's as if we
952  set the betas (backward-probs) on the final frame to the
953  negatives of the corresponding alphas, so all tokens on the last
954  frae will be on a best path.. the extra_cost for each token
955  always corresponds to its alpha+beta on this assumption. We want
956  the final_cost here to correspond to the beta (backward-prob), so
957  we get that by final_cost = extra_cost - tot_cost.
958  [The tot_cost is the forward/alpha cost.]
959  */
960  final_cost = tok->extra_cost - tok->tot_cost;
961  }
962  }
963 
964  StateId state = chunk_lat.AddState();
965  tok2state_map[tok] = state;
966  if (final_cost < std::numeric_limits<BaseFloat>::infinity()) {
967  next_token2label_map[tok] = AllocateNewTokenLabel();
968  StateId token_final_state = chunk_lat.AddState();
969  LatticeArc::Label ilabel = 0,
970  olabel = (next_token2label_map[tok] = AllocateNewTokenLabel());
971  chunk_lat.AddArc(state,
972  LatticeArc(ilabel, olabel,
974  token_final_state));
975  chunk_lat.SetFinal(token_final_state, LatticeWeight(final_cost, 0.0));
976  }
977  }
978  }
979 
980  // Go in reverse order over the remaining frames so we can create arcs as we
981  // go, and their destination-states will already be in the map.
982  for (int32 frame = num_frames_to_include;
983  frame >= num_frames_in_lattice_; frame--) {
984  // The conditional below is needed for the last frame of the utterance.
985  BaseFloat cost_offset = (frame < cost_offsets_.size() ?
986  cost_offsets_[frame] : 0.0);
987 
988  // For the first frame of the chunk, we need to make sure the states are
989  // the ones created by InitializeRawLatticeChunk() (where not pruned away).
990  if (frame == num_frames_in_lattice_ && num_frames_in_lattice_ != 0) {
991  for (Token *tok = active_toks_[frame].toks; tok != NULL; tok = tok->next) {
992  auto iter = token2label_map_.find(tok);
993  KALDI_ASSERT(iter != token2label_map_.end());
994  Label token_label = iter->second;
995  auto iter2 = token_label2state.find(token_label);
996  if (iter2 != token_label2state.end()) {
997  StateId state = iter2->second;
998  tok2state_map[tok] = state;
999  } else {
1000  // Some states may have been pruned out, but we should still allocate
1001  // them. They might have been part of chains of nonemitting arcs
1002  // where the state became disconnected because the last chunk didn't
1003  // include arcs starting at this frame.
1004  StateId state = chunk_lat.AddState();
1005  tok2state_map[tok] = state;
1006  }
1007  }
1008  } else if (frame != num_frames_to_include) { // We already created states
1009  // for the last frame.
1010  for (Token *tok = active_toks_[frame].toks; tok != NULL; tok = tok->next) {
1011  StateId state = chunk_lat.AddState();
1012  tok2state_map[tok] = state;
1013  }
1014  }
1015  for (Token *tok = active_toks_[frame].toks; tok != NULL; tok = tok->next) {
1016  auto iter = tok2state_map.find(tok);
1017  KALDI_ASSERT(iter != tok2state_map.end());
1018  StateId cur_state = iter->second;
1019  for (ForwardLinkT *l = tok->links; l != NULL; l = l->next) {
1020  auto next_iter = tok2state_map.find(l->next_tok);
1021  if (next_iter == tok2state_map.end()) {
1022  // Emitting arcs from the last frame we're including -- ignore
1023  // these.
1024  KALDI_ASSERT(frame == num_frames_to_include);
1025  continue;
1026  }
1027  StateId next_state = next_iter->second;
1028  BaseFloat this_offset = (l->ilabel != 0 ? cost_offset : 0);
1029  LatticeArc arc(l->ilabel, l->olabel,
1030  LatticeWeight(l->graph_cost, l->acoustic_cost - this_offset),
1031  next_state);
1032  // Note: the epsilons get redundantly included at the end and beginning
1033  // of successive chunks. These will get removed in the determinization.
1034  chunk_lat.AddArc(cur_state, arc);
1035  }
1036  }
1037  }
1038  if (num_frames_in_lattice_ == 0) {
1039  // This block locates the start token. NOTE: we use the fact that in the
1040  // linked list of tokens, things are added at the head, so the start state
1041  // must be at the tail. If this data structure is changed in future, we
1042  // might need to explicitly store the start token as a class member.
1043  Token *tok = active_toks_[0].toks;
1044  if (tok == NULL) {
1045  KALDI_WARN << "No tokens exist on start frame";
1046  return determinizer_.GetLattice(); // will be empty.
1047  }
1048  while (tok->next != NULL)
1049  tok = tok->next;
1050  Token *start_token = tok;
1051  auto iter = tok2state_map.find(start_token);
1052  KALDI_ASSERT(iter != tok2state_map.end());
1053  StateId start_state = iter->second;
1054  chunk_lat.SetStart(start_state);
1055  }
1056  token2label_map_.swap(next_token2label_map);
1057 
1058  // bool finished_before_beam =
1060  // We are ignoring the return status, which say whether it finished before the beam.
1061 
1062  num_frames_in_lattice_ = num_frames_to_include;
1063 
1064  if (determinizer_.GetLattice().NumStates() == 0)
1065  return determinizer_.GetLattice(); // Something went wrong, lattice is empty.
1066  }
1067 
1068  unordered_map<Token*, BaseFloat> token2final_cost;
1069  unordered_map<Label, BaseFloat> token_label2final_cost;
1070  if (use_final_probs) {
1071  ComputeFinalCosts(&token2final_cost, NULL, NULL);
1072  for (const auto &p: token2final_cost) {
1073  Token *tok = p.first;
1074  BaseFloat cost = p.second;
1075  auto iter = token2label_map_.find(tok);
1076  if (iter != token2label_map_.end()) {
1077  /* Some tokens may not have survived the pruned determinization. */
1078  Label token_label = iter->second;
1079  bool ret = token_label2final_cost.insert({token_label, cost}).second;
1080  KALDI_ASSERT(ret); /* Make sure it was inserted. */
1081  }
1082  }
1083  }
1084  /* Note: these final-probs won't affect the next chunk, only the lattice
1085  returned from GetLattice(). They are kind of temporaries. */
1086  determinizer_.SetFinalCosts(token_label2final_cost.empty() ? NULL :
1087  &token_label2final_cost);
1088 
1089  return determinizer_.GetLattice();
1090 }
fst::StdArc::StateId StateId
fst::ArcTpl< LatticeWeight > LatticeArc
Definition: kaldi-lattice.h:40
unordered_map< Token *, StateId > temp_token_map_
unordered_map< Token *, Label > token2label_map_
static const LatticeWeightTpl One()
unordered_map< Token *, Label > token2label_map_temp_
void InitializeRawLatticeChunk(Lattice *olat, unordered_map< Label, LatticeArc::StateId > *token_label2state)
Starts the process of creating a raw lattice chunk.
kaldi::int32 int32
void SetFinalCosts(const unordered_map< Label, BaseFloat > *token_label2final_cost=NULL)
fst::LatticeWeightTpl< BaseFloat > LatticeWeight
Definition: kaldi-lattice.h:32
float BaseFloat
Definition: kaldi-types.h:29
int32 num_frames_in_lattice_
num_frames_in_lattice_ is the highest `num_frames_to_include_` argument for any prior call to GetLatt...
fst::VectorFst< LatticeArc > Lattice
Definition: kaldi-lattice.h:44
#define KALDI_ERR
Definition: kaldi-error.h:147
#define KALDI_WARN
Definition: kaldi-error.h:150
fst::StdArc::Label Label
int32 NumFramesDecoded() const
Returns the number of frames decoded so far.
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
static const CompactLatticeWeightTpl< WeightType, IntType > Zero()
LatticeIncrementalDeterminizer determinizer_
Much of the the incremental determinization algorithm is encapsulated in the determinize_ object...
unordered_map< Token *, BaseFloat > final_costs_
bool AcceptRawLatticeChunk(Lattice *raw_fst)
This function accepts the raw FST (state-level lattice) corresponding to a single chunk of the lattic...
void ComputeFinalCosts(unordered_map< Token *, BaseFloat > *final_costs, BaseFloat *final_relative_cost, BaseFloat *final_best_cost) const

◆ GetNumToksForFrame()

int32 GetNumToksForFrame ( int32  frame)
protected

Definition at line 1094 of file lattice-incremental-decoder.cc.

1094  {
1095  int32 r = 0;
1096  for (Token *tok = active_toks_[frame].toks; tok; tok = tok->next) r++;
1097  return r;
1098 }
kaldi::int32 int32

◆ GetOptions()

const LatticeIncrementalDecoderConfig& GetOptions ( ) const
inline

Definition at line 486 of file lattice-incremental-decoder.h.

486 { return config_; }

◆ InitDecoding()

void InitDecoding ( )

InitDecoding initializes the decoding, and should only be used if you intend to call AdvanceDecoding().

If you call Decode(), you don't need to call this. You can also call InitDecoding if you have already decoded an utterance and want to start with a new utterance.

Definition at line 61 of file lattice-incremental-decoder.cc.

61  {
62  // clean up from last time:
64  cost_offsets_.clear();
66  warned_ = false;
67  num_toks_ = 0;
68  decoding_finalized_ = false;
69  final_costs_.clear();
70  StateId start_state = fst_->Start();
71  KALDI_ASSERT(start_state != fst::kNoStateId);
72  active_toks_.resize(1);
73  Token *start_tok = new Token(0.0, 0.0, NULL, NULL, NULL);
74  active_toks_[0].toks = start_tok;
75  toks_.Insert(start_state, start_tok);
76  num_toks_++;
77 
80  token2label_map_.clear();
83 }
fst::StdArc::StateId StateId
Elem * Insert(I key, T val)
Insert inserts a new element into the hashtable/stored list.
unordered_map< Token *, Label > token2label_map_
int32 num_frames_in_lattice_
num_frames_in_lattice_ is the highest `num_frames_to_include_` argument for any prior call to GetLatt...
Elem * Clear()
Clears the hash and gives the head of the current list to the user; ownership is transferred to the u...
Definition: hash-list-inl.h:46
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
LatticeIncrementalDeterminizer determinizer_
Much of the the incremental determinization algorithm is encapsulated in the determinize_ object...
unordered_map< Token *, BaseFloat > final_costs_

◆ KALDI_DISALLOW_COPY_AND_ASSIGN()

KALDI_DISALLOW_COPY_AND_ASSIGN ( LatticeIncrementalDecoderTpl< FST, Token >  )
protected

◆ NumFramesDecoded()

int32 NumFramesDecoded ( ) const
inline

Returns the number of frames decoded so far.

Definition at line 600 of file lattice-incremental-decoder.h.

Referenced by kaldi::DecodeUtteranceLatticeIncremental().

600 { return active_toks_.size() - 1; }

◆ NumFramesInLattice()

int NumFramesInLattice ( ) const
inline

Definition at line 569 of file lattice-incremental-decoder.h.

569 { return num_frames_in_lattice_; }
int32 num_frames_in_lattice_
num_frames_in_lattice_ is the highest `num_frames_to_include_` argument for any prior call to GetLatt...

◆ PossiblyResizeHash()

void PossiblyResizeHash ( size_t  num_toks)
protected

Definition at line 151 of file lattice-incremental-decoder.cc.

151  {
152  size_t new_sz =
153  static_cast<size_t>(static_cast<BaseFloat>(num_toks) * config_.hash_ratio);
154  if (new_sz > toks_.Size()) {
155  toks_.SetSize(new_sz);
156  }
157 }
float BaseFloat
Definition: kaldi-types.h:29
void SetSize(size_t sz)
SetSize tells the object how many hash buckets to allocate (should typically be at least twice the nu...
Definition: hash-list-inl.h:37
size_t Size()
Returns current number of hash buckets.
Definition: hash-list.h:113

◆ ProcessEmitting()

BaseFloat ProcessEmitting ( DecodableInterface decodable)
protected

Definition at line 675 of file lattice-incremental-decoder.cc.

676  {
677  KALDI_ASSERT(active_toks_.size() > 0);
678  int32 frame = active_toks_.size() - 1; // frame is the frame-index
679  // (zero-based) used to get likelihoods
680  // from the decodable object.
681  active_toks_.resize(active_toks_.size() + 1);
682 
683  Elem *final_toks = toks_.Clear(); // analogous to swapping prev_toks_ / cur_toks_
684  // in simple-decoder.h. Removes the Elems from
685  // being indexed in the hash in toks_.
686  Elem *best_elem = NULL;
687  BaseFloat adaptive_beam;
688  size_t tok_cnt;
689  BaseFloat cur_cutoff = GetCutoff(final_toks, &tok_cnt, &adaptive_beam, &best_elem);
690  KALDI_VLOG(6) << "Adaptive beam on frame " << NumFramesDecoded() << " is "
691  << adaptive_beam;
692 
693  PossiblyResizeHash(tok_cnt); // This makes sure the hash is always big enough.
694 
695  BaseFloat next_cutoff = std::numeric_limits<BaseFloat>::infinity();
696  // pruning "online" before having seen all tokens
697 
698  BaseFloat cost_offset = 0.0; // Used to keep probabilities in a good
699  // dynamic range.
700 
701  // First process the best token to get a hopefully
702  // reasonably tight bound on the next cutoff. The only
703  // products of the next block are "next_cutoff" and "cost_offset".
704  if (best_elem) {
705  StateId state = best_elem->key;
706  Token *tok = best_elem->val;
707  cost_offset = -tok->tot_cost;
708  for (fst::ArcIterator<FST> aiter(*fst_, state); !aiter.Done(); aiter.Next()) {
709  const Arc &arc = aiter.Value();
710  if (arc.ilabel != 0) { // propagate..
711  BaseFloat new_weight = arc.weight.Value() + cost_offset -
712  decodable->LogLikelihood(frame, arc.ilabel) +
713  tok->tot_cost;
714  if (new_weight + adaptive_beam < next_cutoff)
715  next_cutoff = new_weight + adaptive_beam;
716  }
717  }
718  }
719 
720  // Store the offset on the acoustic likelihoods that we're applying.
721  // Could just do cost_offsets_.push_back(cost_offset), but we
722  // do it this way as it's more robust to future code changes.
723  cost_offsets_.resize(frame + 1, 0.0);
724  cost_offsets_[frame] = cost_offset;
725 
726  // the tokens are now owned here, in final_toks, and the hash is empty.
727  // 'owned' is a complex thing here; the point is we need to call DeleteElem
728  // on each elem 'e' to let toks_ know we're done with them.
729  for (Elem *e = final_toks, *e_tail; e != NULL; e = e_tail) {
730  // loop this way because we delete "e" as we go.
731  StateId state = e->key;
732  Token *tok = e->val;
733  if (tok->tot_cost <= cur_cutoff) {
734  for (fst::ArcIterator<FST> aiter(*fst_, state); !aiter.Done(); aiter.Next()) {
735  const Arc &arc = aiter.Value();
736  if (arc.ilabel != 0) { // propagate..
737  BaseFloat ac_cost =
738  cost_offset - decodable->LogLikelihood(frame, arc.ilabel),
739  graph_cost = arc.weight.Value(), cur_cost = tok->tot_cost,
740  tot_cost = cur_cost + ac_cost + graph_cost;
741  if (tot_cost >= next_cutoff)
742  continue;
743  else if (tot_cost + adaptive_beam < next_cutoff)
744  next_cutoff = tot_cost + adaptive_beam; // prune by best current token
745  // Note: the frame indexes into active_toks_ are one-based,
746  // hence the + 1.
747  Token *next_tok =
748  FindOrAddToken(arc.nextstate, frame + 1, tot_cost, tok, NULL);
749  // NULL: no change indicator needed
750 
751  // Add ForwardLink from tok to next_tok (put on head of list tok->links)
752  tok->links = new ForwardLinkT(next_tok, arc.ilabel, arc.olabel, graph_cost,
753  ac_cost, tok->links);
754  }
755  } // for all arcs
756  }
757  e_tail = e->tail;
758  toks_.Delete(e); // delete Elem
759  }
760  return next_cutoff;
761 }
fst::StdArc::StateId StateId
BaseFloat GetCutoff(Elem *list_head, size_t *tok_count, BaseFloat *adaptive_beam, Elem **best_elem)
Gets the weight cutoff. Also counts the active tokens.
kaldi::int32 int32
typename HashList< StateId, Token * >::Elem Elem
float BaseFloat
Definition: kaldi-types.h:29
Elem * Clear()
Clears the hash and gives the head of the current list to the user; ownership is transferred to the u...
Definition: hash-list-inl.h:46
Token * FindOrAddToken(StateId state, int32 frame_plus_one, BaseFloat tot_cost, Token *backpointer, bool *changed)
int32 NumFramesDecoded() const
Returns the number of frames decoded so far.
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156
void Delete(Elem *e)
Think of this like delete().
Definition: hash-list-inl.h:66

◆ ProcessNonemitting()

void ProcessNonemitting ( BaseFloat  cost_cutoff)
protected

Definition at line 776 of file lattice-incremental-decoder.cc.

776  {
777  KALDI_ASSERT(!active_toks_.empty());
778  int32 frame = static_cast<int32>(active_toks_.size()) - 2;
779  // Note: "frame" is the time-index we just processed, or -1 if
780  // we are processing the nonemitting transitions before the
781  // first frame (called from InitDecoding()).
782 
783  // Processes nonemitting arcs for one frame. Propagates within toks_.
784  // Note-- this queue structure is is not very optimal as
785  // it may cause us to process states unnecessarily (e.g. more than once),
786  // but in the baseline code, turning this vector into a set to fix this
787  // problem did not improve overall speed.
788 
789  KALDI_ASSERT(queue_.empty());
790 
791  if (toks_.GetList() == NULL) {
792  if (!warned_) {
793  KALDI_WARN << "Error, no surviving tokens: frame is " << frame;
794  warned_ = true;
795  }
796  }
797 
798  for (const Elem *e = toks_.GetList(); e != NULL; e = e->tail) {
799  StateId state = e->key;
800  if (fst_->NumInputEpsilons(state) != 0) queue_.push_back(state);
801  }
802 
803  while (!queue_.empty()) {
804  StateId state = queue_.back();
805  queue_.pop_back();
806 
807  Token *tok =
808  toks_.Find(state)
809  ->val; // would segfault if state not in toks_ but this can't happen.
810  BaseFloat cur_cost = tok->tot_cost;
811  if (cur_cost >= cutoff) // Don't bother processing successors.
812  continue;
813  // If "tok" has any existing forward links, delete them,
814  // because we're about to regenerate them. This is a kind
815  // of non-optimality (remember, this is the simple decoder),
816  // but since most states are emitting it's not a huge issue.
817  DeleteForwardLinks(tok); // necessary when re-visiting
818  tok->links = NULL;
819  for (fst::ArcIterator<FST> aiter(*fst_, state); !aiter.Done(); aiter.Next()) {
820  const Arc &arc = aiter.Value();
821  if (arc.ilabel == 0) { // propagate nonemitting only...
822  BaseFloat graph_cost = arc.weight.Value(), tot_cost = cur_cost + graph_cost;
823  if (tot_cost < cutoff) {
824  bool changed;
825 
826  Token *new_tok =
827  FindOrAddToken(arc.nextstate, frame + 1, tot_cost, tok, &changed);
828 
829  tok->links =
830  new ForwardLinkT(new_tok, 0, arc.olabel, graph_cost, 0, tok->links);
831 
832  // "changed" tells us whether the new token has a different
833  // cost from before, or is new [if so, add into queue].
834  if (changed && fst_->NumInputEpsilons(arc.nextstate) != 0)
835  queue_.push_back(arc.nextstate);
836  }
837  }
838  } // for all arcs
839  } // while queue not empty
840 }
fst::StdArc::StateId StateId
kaldi::int32 int32
typename HashList< StateId, Token * >::Elem Elem
float BaseFloat
Definition: kaldi-types.h:29
const Elem * GetList() const
Gives the head of the current list to the user.
Definition: hash-list-inl.h:61
#define KALDI_WARN
Definition: kaldi-error.h:150
Token * FindOrAddToken(StateId state, int32 frame_plus_one, BaseFloat tot_cost, Token *backpointer, bool *changed)
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
Elem * Find(I key)
Find tries to find this element in the current list using the hashtable.
Definition: hash-list-inl.h:72
static void DeleteForwardLinks(Token *tok)
NOTE: for parts the internal implementation that are shared with LatticeFasterDecoer, we have removed the comments.

◆ PruneActiveTokens()

void PruneActiveTokens ( BaseFloat  delta)
protected

Definition at line 451 of file lattice-incremental-decoder.cc.

451  {
452  int32 cur_frame_plus_one = NumFramesDecoded();
453  int32 num_toks_begin = num_toks_;
454 
455  if (active_toks_[cur_frame_plus_one].num_toks == -1){
456  // The current frame's tokens don't get pruned so they don't get counted
457  // (the count is needed by the incremental determinization code).
458  // Fix this.
459  int this_frame_num_toks = 0;
460  for (Token *t = active_toks_[cur_frame_plus_one].toks; t != NULL; t = t->next)
461  this_frame_num_toks++;
462  active_toks_[cur_frame_plus_one].num_toks = this_frame_num_toks;
463  }
464 
465  // The index "f" below represents a "frame plus one", i.e. you'd have to subtract
466  // one to get the corresponding index for the decodable object.
467  for (int32 f = cur_frame_plus_one - 1; f >= 0; f--) {
468  // Reason why we need to prune forward links in this situation:
469  // (1) we have never pruned them (new TokenList)
470  // (2) we have not yet pruned the forward links to the next f,
471  // after any of those tokens have changed their extra_cost.
472  if (active_toks_[f].must_prune_forward_links) {
473  bool extra_costs_changed = false, links_pruned = false;
474  PruneForwardLinks(f, &extra_costs_changed, &links_pruned, delta);
475  if (extra_costs_changed && f > 0) // any token has changed extra_cost
476  active_toks_[f - 1].must_prune_forward_links = true;
477  if (links_pruned) // any link was pruned
478  active_toks_[f].must_prune_tokens = true;
479  active_toks_[f].must_prune_forward_links = false; // job done
480  }
481  if (f + 1 < cur_frame_plus_one && // except for last f (no forward links)
482  active_toks_[f + 1].must_prune_tokens) {
483  PruneTokensForFrame(f + 1);
484  active_toks_[f + 1].must_prune_tokens = false;
485  }
486  }
487  KALDI_VLOG(4) << "pruned tokens from " << num_toks_begin
488  << " to " << num_toks_;
489 }
kaldi::int32 int32
void PruneForwardLinks(int32 frame_plus_one, bool *extra_costs_changed, bool *links_pruned, BaseFloat delta)
int32 NumFramesDecoded() const
Returns the number of frames decoded so far.
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156

◆ PruneForwardLinks()

void PruneForwardLinks ( int32  frame_plus_one,
bool extra_costs_changed,
bool links_pruned,
BaseFloat  delta 
)
protected

Definition at line 243 of file lattice-incremental-decoder.cc.

245  {
246  // delta is the amount by which the extra_costs must change
247  // If delta is larger, we'll tend to go back less far
248  // toward the beginning of the file.
249  // extra_costs_changed is set to true if extra_cost was changed for any token
250  // links_pruned is set to true if any link in any token was pruned
251 
252  *extra_costs_changed = false;
253  *links_pruned = false;
254  KALDI_ASSERT(frame_plus_one >= 0 && frame_plus_one < active_toks_.size());
255  if (active_toks_[frame_plus_one].toks == NULL) { // empty list; should not happen.
256  if (!warned_) {
257  KALDI_WARN << "No tokens alive [doing pruning].. warning first "
258  "time only for each utterance\n";
259  warned_ = true;
260  }
261  }
262 
263  // We have to iterate until there is no more change, because the links
264  // are not guaranteed to be in topological order.
265  bool changed = true; // difference new minus old extra cost >= delta ?
266  while (changed) {
267  changed = false;
268  for (Token *tok = active_toks_[frame_plus_one].toks; tok != NULL;
269  tok = tok->next) {
270  ForwardLinkT *link, *prev_link = NULL;
271  // will recompute tok_extra_cost for tok.
272  BaseFloat tok_extra_cost = std::numeric_limits<BaseFloat>::infinity();
273  // tok_extra_cost is the best (min) of link_extra_cost of outgoing links
274  for (link = tok->links; link != NULL;) {
275  // See if we need to excise this link...
276  Token *next_tok = link->next_tok;
277  BaseFloat link_extra_cost =
278  next_tok->extra_cost +
279  ((tok->tot_cost + link->acoustic_cost + link->graph_cost) -
280  next_tok->tot_cost); // difference in brackets is >= 0
281  // link_exta_cost is the difference in score between the best paths
282  // through link source state and through link destination state
283  KALDI_ASSERT(link_extra_cost == link_extra_cost); // check for NaN
284  if (link_extra_cost > config_.lattice_beam) { // excise link
285  ForwardLinkT *next_link = link->next;
286  if (prev_link != NULL)
287  prev_link->next = next_link;
288  else
289  tok->links = next_link;
290  delete link;
291  link = next_link; // advance link but leave prev_link the same.
292  *links_pruned = true;
293  } else { // keep the link and update the tok_extra_cost if needed.
294  if (link_extra_cost < 0.0) { // this is just a precaution.
295  if (link_extra_cost < -0.01)
296  KALDI_WARN << "Negative extra_cost: " << link_extra_cost;
297  link_extra_cost = 0.0;
298  }
299  if (link_extra_cost < tok_extra_cost) tok_extra_cost = link_extra_cost;
300  prev_link = link; // move to next link
301  link = link->next;
302  }
303  } // for all outgoing links
304  if (fabs(tok_extra_cost - tok->extra_cost) > delta)
305  changed = true; // difference new minus old is bigger than delta
306  tok->extra_cost = tok_extra_cost;
307  // will be +infinity or <= lattice_beam_.
308  // infinity indicates, that no forward link survived pruning
309  } // for all Token on active_toks_[frame]
310  if (changed) *extra_costs_changed = true;
311 
312  // Note: it's theoretically possible that aggressive compiler
313  // optimizations could cause an infinite loop here for small delta and
314  // high-dynamic-range scores.
315  } // while changed
316 }
float BaseFloat
Definition: kaldi-types.h:29
#define KALDI_WARN
Definition: kaldi-error.h:150
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ PruneForwardLinksFinal()

void PruneForwardLinksFinal ( )
protected

Definition at line 322 of file lattice-incremental-decoder.cc.

322  {
323  KALDI_ASSERT(!active_toks_.empty());
324  int32 frame_plus_one = active_toks_.size() - 1;
325 
326  if (active_toks_[frame_plus_one].toks == NULL) // empty list; should not happen.
327  KALDI_WARN << "No tokens alive at end of file";
328 
329  typedef typename unordered_map<Token *, BaseFloat>::const_iterator IterType;
331  decoding_finalized_ = true;
332  // We call DeleteElems() as a nicety, not because it's really necessary;
333  // otherwise there would be a time, after calling PruneTokensForFrame() on the
334  // final frame, when toks_.GetList() or toks_.Clear() would contain pointers
335  // to nonexistent tokens.
337 
338  // Now go through tokens on this frame, pruning forward links... may have to
339  // iterate a few times until there is no more change, because the list is not
340  // in topological order. This is a modified version of the code in
341  // PruneForwardLinks, but here we also take account of the final-probs.
342  bool changed = true;
343  BaseFloat delta = 1.0e-05;
344  while (changed) {
345  changed = false;
346  for (Token *tok = active_toks_[frame_plus_one].toks; tok != NULL;
347  tok = tok->next) {
348  ForwardLinkT *link, *prev_link = NULL;
349  // will recompute tok_extra_cost. It has a term in it that corresponds
350  // to the "final-prob", so instead of initializing tok_extra_cost to infinity
351  // below we set it to the difference between the (score+final_prob) of this
352  // token,
353  // and the best such (score+final_prob).
354  BaseFloat final_cost;
355  if (final_costs_.empty()) {
356  final_cost = 0.0;
357  } else {
358  IterType iter = final_costs_.find(tok);
359  if (iter != final_costs_.end())
360  final_cost = iter->second;
361  else
362  final_cost = std::numeric_limits<BaseFloat>::infinity();
363  }
364  BaseFloat tok_extra_cost = tok->tot_cost + final_cost - final_best_cost_;
365  // tok_extra_cost will be a "min" over either directly being final, or
366  // being indirectly final through other links, and the loop below may
367  // decrease its value:
368  for (link = tok->links; link != NULL;) {
369  // See if we need to excise this link...
370  Token *next_tok = link->next_tok;
371  BaseFloat link_extra_cost =
372  next_tok->extra_cost +
373  ((tok->tot_cost + link->acoustic_cost + link->graph_cost) -
374  next_tok->tot_cost);
375  if (link_extra_cost > config_.lattice_beam) { // excise link
376  ForwardLinkT *next_link = link->next;
377  if (prev_link != NULL)
378  prev_link->next = next_link;
379  else
380  tok->links = next_link;
381  delete link;
382  link = next_link; // advance link but leave prev_link the same.
383  } else { // keep the link and update the tok_extra_cost if needed.
384  if (link_extra_cost < 0.0) { // this is just a precaution.
385  if (link_extra_cost < -0.01)
386  KALDI_WARN << "Negative extra_cost: " << link_extra_cost;
387  link_extra_cost = 0.0;
388  }
389  if (link_extra_cost < tok_extra_cost) tok_extra_cost = link_extra_cost;
390  prev_link = link;
391  link = link->next;
392  }
393  }
394  // prune away tokens worse than lattice_beam above best path. This step
395  // was not necessary in the non-final case because then, this case
396  // showed up as having no forward links. Here, the tok_extra_cost has
397  // an extra component relating to the final-prob.
398  if (tok_extra_cost > config_.lattice_beam)
399  tok_extra_cost = std::numeric_limits<BaseFloat>::infinity();
400  // to be pruned in PruneTokensForFrame
401 
402  if (!ApproxEqual(tok->extra_cost, tok_extra_cost, delta)) changed = true;
403  tok->extra_cost = tok_extra_cost; // will be +infinity or <= lattice_beam_.
404  }
405  } // while changed
406 }
kaldi::int32 int32
float BaseFloat
Definition: kaldi-types.h:29
#define KALDI_WARN
Definition: kaldi-error.h:150
Elem * Clear()
Clears the hash and gives the head of the current list to the user; ownership is transferred to the u...
Definition: hash-list-inl.h:46
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
static bool ApproxEqual(float a, float b, float relative_tolerance=0.001)
return abs(a - b) <= relative_tolerance * (abs(a)+abs(b)).
Definition: kaldi-math.h:265
unordered_map< Token *, BaseFloat > final_costs_
void ComputeFinalCosts(unordered_map< Token *, BaseFloat > *final_costs, BaseFloat *final_relative_cost, BaseFloat *final_best_cost) const

◆ PruneTokensForFrame()

void PruneTokensForFrame ( int32  frame_plus_one)
protected

Definition at line 420 of file lattice-incremental-decoder.cc.

421  {
422  KALDI_ASSERT(frame_plus_one >= 0 && frame_plus_one < active_toks_.size());
423  Token *&toks = active_toks_[frame_plus_one].toks;
424  if (toks == NULL) KALDI_WARN << "No tokens alive [doing pruning]";
425  Token *tok, *next_tok, *prev_tok = NULL;
426  int32 num_toks = 0;
427  for (tok = toks; tok != NULL; tok = next_tok, num_toks++) {
428  next_tok = tok->next;
429  if (tok->extra_cost == std::numeric_limits<BaseFloat>::infinity()) {
430  // token is unreachable from end of graph; (no forward links survived)
431  // excise tok from list and delete tok.
432  if (prev_tok != NULL)
433  prev_tok->next = tok->next;
434  else
435  toks = tok->next;
436  delete tok;
437  num_toks_--;
438  } else { // fetch next Token
439  prev_tok = tok;
440  }
441  }
442  active_toks_[frame_plus_one].num_toks = num_toks;
443 }
kaldi::int32 int32
#define KALDI_WARN
Definition: kaldi-error.h:150
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ ReachedFinal()

bool ReachedFinal ( ) const
inline

says whether a final-state was active on the last frame.

If it was not, the lattice (or traceback) will end with states that are not final-states.

Definition at line 514 of file lattice-incremental-decoder.h.

Referenced by kaldi::DecodeUtteranceLatticeIncremental().

514  {
515  return FinalRelativeCost() != std::numeric_limits<BaseFloat>::infinity();
516  }
BaseFloat FinalRelativeCost() const
FinalRelativeCost() serves the same purpose as ReachedFinal(), but gives more information.

◆ SetOptions()

void SetOptions ( const LatticeIncrementalDecoderConfig config)
inline

Definition at line 484 of file lattice-incremental-decoder.h.

484 { config_ = config; }

◆ UpdateLatticeDeterminization()

void UpdateLatticeDeterminization ( )
protected

UpdateLatticeDeterminization() ensures the work of determinization is kept up to date so that when you do need the lattice you can get it fast.

It uses the configuration values `determinize_delay`, `determinize_max_delay` and `determinize_min_chunk_size` to decide whether and when to call GetLattice(). You can safely call this as often as you want (e.g. after each time you call AdvanceDecoding(); it won't do subtantially more work if it is called frequently.

Definition at line 86 of file lattice-incremental-decoder.cc.

86  {
89  return;
90 
91 
92  /* Make sure the token-pruning is active. Note: PruneActiveTokens() has
93  internal logic that prevents it from doing unnecessary work if you
94  call it and then immediately call it again. */
96 
98  last = NumFramesDecoded(),
99  fewest_tokens = std::numeric_limits<int32>::max(),
100  best_frame = -1;
101  for (int32 t = last; t >= first; t--) {
102  /* Make sure PruneActiveTokens() has computed num_toks for all these
103  frames... */
104  KALDI_ASSERT(active_toks_[t].num_toks != -1);
105  if (active_toks_[t].num_toks < fewest_tokens) {
106  // <= because we want the latest one in case of ties.
107  fewest_tokens = active_toks_[t].num_toks;
108  best_frame = t;
109  }
110  }
111  /* OK, determinize the chunk that spans from num_frames_in_lattice_ to
112  best_frame. */
113  bool use_final_probs = false;
114  GetLattice(best_frame, use_final_probs);
115  return;
116 }
kaldi::int32 int32
int32 num_frames_in_lattice_
num_frames_in_lattice_ is the highest `num_frames_to_include_` argument for any prior call to GetLatt...
int32 NumFramesDecoded() const
Returns the number of frames decoded so far.
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
const CompactLattice & GetLattice(int32 num_frames_to_include, bool use_final_probs=false)
This decoder has no GetBestPath() function.

Member Data Documentation

◆ active_toks_

std::vector<TokenList> active_toks_
protected

Definition at line 643 of file lattice-incremental-decoder.h.

◆ config_

Definition at line 661 of file lattice-incremental-decoder.h.

◆ cost_offsets_

std::vector<BaseFloat> cost_offsets_
protected

Definition at line 648 of file lattice-incremental-decoder.h.

◆ decoding_finalized_

bool decoding_finalized_
protected

Definition at line 651 of file lattice-incremental-decoder.h.

◆ delete_fst_

bool delete_fst_
protected

Definition at line 647 of file lattice-incremental-decoder.h.

◆ determinizer_

LatticeIncrementalDeterminizer determinizer_
protected

Much of the the incremental determinization algorithm is encapsulated in the determinize_ object.

Definition at line 664 of file lattice-incremental-decoder.h.

◆ final_best_cost_

BaseFloat final_best_cost_
protected

Definition at line 655 of file lattice-incremental-decoder.h.

◆ final_costs_

unordered_map<Token *, BaseFloat> final_costs_
protected

Definition at line 653 of file lattice-incremental-decoder.h.

◆ final_relative_cost_

BaseFloat final_relative_cost_
protected

Definition at line 654 of file lattice-incremental-decoder.h.

◆ fst_

const FST* fst_
protected

Definition at line 646 of file lattice-incremental-decoder.h.

◆ next_token_label_

Label next_token_label_
protected

Definition at line 682 of file lattice-incremental-decoder.h.

◆ num_frames_in_lattice_

int32 num_frames_in_lattice_
protected

num_frames_in_lattice_ is the highest `num_frames_to_include_` argument for any prior call to GetLattice().

Definition at line 672 of file lattice-incremental-decoder.h.

◆ num_toks_

int32 num_toks_
protected

Definition at line 649 of file lattice-incremental-decoder.h.

◆ queue_

std::vector<StateId> queue_
protected

Definition at line 644 of file lattice-incremental-decoder.h.

◆ temp_token_map_

unordered_map<Token*, StateId> temp_token_map_
protected

Definition at line 668 of file lattice-incremental-decoder.h.

◆ tmp_array_

std::vector<BaseFloat> tmp_array_
protected

Definition at line 645 of file lattice-incremental-decoder.h.

◆ token2label_map_

unordered_map<Token*, Label> token2label_map_
protected

Definition at line 676 of file lattice-incremental-decoder.h.

◆ token2label_map_temp_

unordered_map<Token*, Label> token2label_map_temp_
protected

Definition at line 679 of file lattice-incremental-decoder.h.

◆ toks_

HashList<StateId, Token *> toks_
protected

Definition at line 642 of file lattice-incremental-decoder.h.

◆ warned_

bool warned_
protected

Definition at line 650 of file lattice-incremental-decoder.h.


The documentation for this class was generated from the following files: