grammar-fst.cc
Go to the documentation of this file.
1 // decoder/grammar-fst.cc
2 
3 // Copyright 2018 Johns Hopkins University (author: Daniel Povey)
4 
5 // See ../../COPYING for clarification regarding multiple authors
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
15 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
16 // MERCHANTABLITY OR NON-INFRINGEMENT.
17 // See the Apache 2 License for the specific language governing permissions and
18 // limitations under the License.
19 
20 #include "decoder/grammar-fst.h"
22 
23 namespace fst {
24 
25 
27  int32 nonterm_phones_offset,
28  std::shared_ptr<const ConstFst<StdArc> > top_fst,
29  const std::vector<std::pair<Label, std::shared_ptr<const ConstFst<StdArc> > > > &ifsts):
30  nonterm_phones_offset_(nonterm_phones_offset),
31  top_fst_(top_fst),
32  ifsts_(ifsts) {
33  Init();
34 }
35 
37  KALDI_ASSERT(nonterm_phones_offset_ > 1);
38  InitNonterminalMap();
39  entry_arcs_.resize(ifsts_.size());
40  if (!ifsts_.empty()) {
41  // We call this mostly so that if something is wrong with the input FSTs, the
42  // problem will be detected sooner rather than later.
43  // There would be no problem if we were to call InitEntryArcs(i)
44  // for all 0 <= i < ifsts_size(), but we choose to call it
45  // lazily on demand, to save startup time if the number of nonterminals
46  // is large.
47  InitEntryArcs(0);
48  }
49  InitInstances();
50 }
51 
53  Destroy();
54 }
55 
57  for (size_t i = 0; i < instances_.size(); i++) {
58  FstInstance &instance = instances_[i];
59  std::unordered_map<BaseStateId, ExpandedState*>::const_iterator
60  iter = instance.expanded_states.begin(),
61  end = instance.expanded_states.end();
62  for (; iter != end; ++iter) {
63  ExpandedState *e = iter->second;
64  delete e;
65  }
66  }
67  top_fst_ = NULL;
68  ifsts_.clear();
69  nonterminal_map_.clear();
70  entry_arcs_.clear();
71  instances_.clear();
72 }
73 
74 
76  int32 *nonterminal_symbol,
77  int32 *left_context_phone) {
78  // encoding_multiple will normally equal 1000 (but may be a multiple of 1000
79  // if there are a lot of phones); kNontermBigNumber is 10000000.
80  int32 big_number = static_cast<int32>(kNontermBigNumber),
81  nonterm_phones_offset = nonterm_phones_offset_,
82  encoding_multiple = GetEncodingMultiple(nonterm_phones_offset);
83  // The following assertion should be optimized out as the condition is
84  // statically known.
85  KALDI_ASSERT(big_number % static_cast<int32>(kNontermMediumNumber) == 0);
86 
87  *nonterminal_symbol = (label - big_number) / encoding_multiple;
88  *left_context_phone = label % encoding_multiple;
89  if (*nonterminal_symbol <= nonterm_phones_offset ||
90  *left_context_phone == 0 || *left_context_phone >
91  nonterm_phones_offset + static_cast<int32>(kNontermBos))
92  KALDI_ERR << "Decoding invalid label " << label
93  << ": code error or invalid --nonterm-phones-offset?";
94 
95 }
96 
98  nonterminal_map_.clear();
99  for (size_t i = 0; i < ifsts_.size(); i++) {
100  int32 nonterminal = ifsts_[i].first;
101  if (nonterminal_map_.count(nonterminal))
102  KALDI_ERR << "Nonterminal symbol " << nonterminal
103  << " is paired with two FSTs.";
104  if (nonterminal < GetPhoneSymbolFor(kNontermUserDefined))
105  KALDI_ERR << "Nonterminal symbol " << nonterminal
106  << " in input pairs, was expected to be >= "
107  << GetPhoneSymbolFor(kNontermUserDefined);
108  nonterminal_map_[nonterminal] = static_cast<int32>(i);
109  }
110 }
111 
112 
114  KALDI_ASSERT(static_cast<size_t>(i) < ifsts_.size());
115  const ConstFst<StdArc> &fst = *(ifsts_[i].second);
116  if (fst.NumStates() == 0)
117  return false; /* this was the empty FST. */
118  InitEntryOrReentryArcs(fst, fst.Start(),
119  GetPhoneSymbolFor(kNontermBegin),
120  &(entry_arcs_[i]));
121  return true;
122 }
123 
125  KALDI_ASSERT(instances_.empty());
126  instances_.resize(1);
127  instances_[0].ifst_index = -1;
128  instances_[0].fst = top_fst_.get();
129  instances_[0].parent_instance = -1;
130  instances_[0].parent_state = -1;
131 }
132 
134  const ConstFst<StdArc> &fst,
135  int32 entry_state,
136  int32 expected_nonterminal_symbol,
137  std::unordered_map<int32, int32> *phone_to_arc) {
138  phone_to_arc->clear();
139  ArcIterator<ConstFst<StdArc> > aiter(fst, entry_state);
140  int32 arc_index = 0;
141  for (; !aiter.Done(); aiter.Next(), ++arc_index) {
142  const StdArc &arc = aiter.Value();
143  int32 nonterminal, left_context_phone;
144  if (arc.ilabel <= (int32)kNontermBigNumber) {
145  if (entry_state == fst.Start()) {
146  KALDI_ERR << "There is something wrong with the graph; did you forget to "
147  "add #nonterm_begin and #nonterm_end to the non-top-level FSTs "
148  "before compiling?";
149  } else {
150  KALDI_ERR << "There is something wrong with the graph; re-entry state is "
151  "not as anticipated.";
152  }
153  }
154  DecodeSymbol(arc.ilabel, &nonterminal, &left_context_phone);
155  if (nonterminal != expected_nonterminal_symbol) {
156  KALDI_ERR << "Expected arcs from this state to have nonterminal-symbol "
157  << expected_nonterminal_symbol << ", but got "
158  << nonterminal;
159  }
160  std::pair<int32, int32> p(left_context_phone, arc_index);
161  if (!phone_to_arc->insert(p).second) {
162  // If it was not successfully inserted in the phone_to_arc map, it means
163  // there were two arcs with the same left-context phone, which does not
164  // make sense; that's an error, likely a code error (or an error when the
165  // input FSTs were generated).
166  KALDI_ERR << "Two arcs had the same left-context phone.";
167  }
168  }
169 }
170 
172  int32 instance_id, BaseStateId state_id) {
173  int32 big_number = kNontermBigNumber;
174  const ConstFst<StdArc> &fst = *(instances_[instance_id].fst);
175  ArcIterator<ConstFst<StdArc> > aiter(fst, state_id);
176  KALDI_ASSERT(!aiter.Done() && aiter.Value().ilabel > big_number &&
177  "Something is not right; did you call PrepareForGrammarFst()?");
178 
179  const StdArc &arc = aiter.Value();
180  int32 encoding_multiple = GetEncodingMultiple(nonterm_phones_offset_),
181  nonterminal = (arc.ilabel - big_number) / encoding_multiple;
182  if (nonterminal == GetPhoneSymbolFor(kNontermBegin) ||
183  nonterminal == GetPhoneSymbolFor(kNontermReenter)) {
184  KALDI_ERR << "Encountered unexpected type of nonterminal while "
185  "expanding state.";
186  } else if (nonterminal == GetPhoneSymbolFor(kNontermEnd)) {
187  return ExpandStateEnd(instance_id, state_id);
188  } else if (nonterminal >= GetPhoneSymbolFor(kNontermUserDefined)) {
189  return ExpandStateUserDefined(instance_id, state_id);
190  } else {
191  KALDI_ERR << "Encountered unexpected type of nonterminal "
192  << nonterminal << " while expanding state.";
193  }
194  return NULL; // Suppress compiler warning
195 }
196 
197 
198 // static inline
199 void GrammarFst::CombineArcs(const StdArc &leaving_arc,
200  const StdArc &arriving_arc,
201  float cost_correction,
202  StdArc *arc) {
203  // The following assertion shouldn't fail; we ensured this in
204  // PrepareForGrammarFst(), search for 'olabel_problem'.
205  KALDI_ASSERT(leaving_arc.olabel == 0);
206  // 'leaving_arc' leaves one fst, and 'arriving_arcs', conceptually arrives in
207  // another. This code merges the information of the two arcs to make a
208  // cross-FST arc. The ilabel information is discarded as it was only intended
209  // for the consumption of the GrammarFST code.
210  arc->ilabel = 0;
211  arc->olabel = arriving_arc.olabel;
212  // conceptually, arc->weight =
213  // Times(Times(leaving_arc.weight, arriving_arc.weight), Weight(cost_correction)).
214  // The below might be a bit faster, I hope-- avoiding checking.
215  arc->weight = Weight(cost_correction + leaving_arc.weight.Value() +
216  arriving_arc.weight.Value());
217  arc->nextstate = arriving_arc.nextstate;
218 }
219 
221  int32 instance_id, BaseStateId state_id) {
222  if (instance_id == 0)
223  KALDI_ERR << "Did not expect #nonterm_end symbol in FST-instance 0.";
224  const FstInstance &instance = instances_[instance_id];
225  int32 parent_instance_id = instance.parent_instance;
226  const ConstFst<StdArc> &fst = *(instance.fst);
227  const FstInstance &parent_instance = instances_[parent_instance_id];
228  const ConstFst<StdArc> &parent_fst = *(parent_instance.fst);
229 
230  ExpandedState *ans = new ExpandedState;
231  ans->dest_fst_instance = parent_instance_id;
232 
233  // parent_aiter is the arc-iterator in the state we return to. We'll Seek()
234  // to a different position 'parent_aiter' for each arc leaving this state.
235  // (actually we expect just one arc to leave this state).
236  ArcIterator<ConstFst<StdArc> > parent_aiter(parent_fst,
237  instance.parent_state);
238 
239  // for explanation of cost_correction, see documentation for CombineArcs().
240  float num_reentry_arcs = instances_[instance_id].parent_reentry_arcs.size(),
241  cost_correction = -log(num_reentry_arcs);
242 
243  ArcIterator<ConstFst<StdArc> > aiter(fst, state_id);
244 
245  for (; !aiter.Done(); aiter.Next()) {
246  const StdArc &leaving_arc = aiter.Value();
247  int32 this_nonterminal, left_context_phone;
248  DecodeSymbol(leaving_arc.ilabel, &this_nonterminal,
249  &left_context_phone);
250  KALDI_ASSERT(this_nonterminal == GetPhoneSymbolFor(kNontermEnd) &&
251  ">1 nonterminals from a state; did you use "
252  "PrepareForGrammarFst()?");
253  std::unordered_map<int32, int32>::const_iterator reentry_iter =
254  instances_[instance_id].parent_reentry_arcs.find(left_context_phone),
255  reentry_end = instances_[instance_id].parent_reentry_arcs.end();
256  if (reentry_iter == reentry_end) {
257  KALDI_ERR << "FST with index " << instance.ifst_index
258  << " ends with left-context-phone " << left_context_phone
259  << " but parent FST does not support that left-context "
260  "at the return point.";
261  }
262  size_t parent_arc_index = static_cast<size_t>(reentry_iter->second);
263  parent_aiter.Seek(parent_arc_index);
264  const StdArc &arriving_arc = parent_aiter.Value();
265  // 'arc' will combine the information on 'leaving_arc' and 'arriving_arc',
266  // except that the ilabel will be set to zero.
267  if (leaving_arc.olabel != 0) {
268  // If the following fails it would maybe indicate you hadn't called
269  // PrepareForGrammarFst(), or there was an error in that, because
270  // we made sure the leaving arc does not have an olabel. Search
271  // in that code for 'olabel_problem' for more details.
272  KALDI_ERR << "Leaving arc has zero olabel.";
273  }
274  StdArc arc;
275  CombineArcs(leaving_arc, arriving_arc, cost_correction, &arc);
276  ans->arcs.push_back(arc);
277  }
278  return ans;
279 }
280 
282  int32 state) {
283  int64 encoded_pair = (static_cast<int64>(nonterminal) << 32) + state;
284  // 'new_instance_id' is the instance-id we'd assign if we had to create a new one.
285  // We try to add it at once, to avoid having to do an extra map lookup in case
286  // it wasn't there and we did need to add it.
287  int32 child_instance_id = instances_.size();
288  {
289  std::pair<int64, int32> p(encoded_pair, child_instance_id);
290  std::pair<std::unordered_map<int64, int32>::const_iterator, bool> ans =
291  instances_[instance_id].child_instances.insert(p);
292  if (!ans.second) {
293  // The pair was not inserted, which means the key 'encoded_pair' did exist in the
294  // map. Return the value in the map.
295  child_instance_id = ans.first->second;
296  return child_instance_id;
297  }
298  }
299  // If we reached this point, we did successfully insert 'child_instance_id' into
300  // the map, because the key didn't exist. That means we have to actually create
301  // the instance.
302  instances_.resize(child_instance_id + 1);
303  const FstInstance &parent_instance = instances_[instance_id];
304  FstInstance &child_instance = instances_[child_instance_id];
305 
306  // Work out the ifst_index for this nonterminal.
307  std::unordered_map<int32, int32>::const_iterator iter =
308  nonterminal_map_.find(nonterminal);
309  if (iter == nonterminal_map_.end()) {
310  KALDI_ERR << "Nonterminal " << nonterminal << " was requested, but "
311  "there is no FST for it.";
312  }
313  int32 ifst_index = iter->second;
314  child_instance.ifst_index = ifst_index;
315  child_instance.fst = ifsts_[ifst_index].second.get();
316  child_instance.parent_instance = instance_id;
317  child_instance.parent_state = state;
318  InitEntryOrReentryArcs(*(parent_instance.fst), state,
319  GetPhoneSymbolFor(kNontermReenter),
320  &(child_instance.parent_reentry_arcs));
321  return child_instance_id;
322 }
323 
325  int32 instance_id, BaseStateId state_id) {
326  const ConstFst<StdArc> &fst = *(instances_[instance_id].fst);
327  ArcIterator<ConstFst<StdArc> > aiter(fst, state_id);
328 
329  ExpandedState *ans = new ExpandedState;
330  int32 dest_fst_instance = -1; // We'll set it in the loop.
331  // and->dest_fst_instance will be set to this.
332 
333  for (; !aiter.Done(); aiter.Next()) {
334  const StdArc &leaving_arc = aiter.Value();
335  int32 nonterminal, left_context_phone;
336  DecodeSymbol(leaving_arc.ilabel, &nonterminal,
337  &left_context_phone);
338  int32 child_instance_id = GetChildInstanceId(instance_id,
339  nonterminal,
340  leaving_arc.nextstate);
341  if (dest_fst_instance < 0) {
342  dest_fst_instance = child_instance_id;
343  } else if (dest_fst_instance != child_instance_id) {
344  KALDI_ERR << "Same state leaves to different FST instances "
345  "(Did you use PrepareForGrammarFst()?)";
346  }
347  const FstInstance &child_instance = instances_[child_instance_id];
348  const ConstFst<StdArc> &child_fst = *(child_instance.fst);
349  int32 child_ifst_index = child_instance.ifst_index;
350  std::unordered_map<int32, int32> &entry_arcs = entry_arcs_[child_ifst_index];
351  if (entry_arcs.empty()) {
352  if (!InitEntryArcs(child_ifst_index)) {
353  // This child-FST was the empty FST. There are no arcs to expand.
354  continue;
355  }
356  }
357  // for explanation of cost_correction, see documentation for CombineArcs().
358  float num_entry_arcs = entry_arcs.size(),
359  cost_correction = -log(num_entry_arcs);
360 
361  // Get the arc-index for the arc leaving the start-state of child FST that
362  // corresponds to this phonetic context.
363  std::unordered_map<int32, int32>::const_iterator entry_iter =
364  entry_arcs.find(left_context_phone);
365  if (entry_iter == entry_arcs.end()) {
366  KALDI_ERR << "FST for nonterminal " << nonterminal
367  << " does not have an entry point for left-context-phone "
368  << left_context_phone;
369  }
370  int32 arc_index = entry_iter->second;
371  ArcIterator<ConstFst<StdArc> > child_aiter(child_fst, child_fst.Start());
372  child_aiter.Seek(arc_index);
373  const StdArc &arriving_arc = child_aiter.Value();
374  StdArc arc;
375  CombineArcs(leaving_arc, arriving_arc, cost_correction, &arc);
376  ans->arcs.push_back(arc);
377  }
378  ans->dest_fst_instance = dest_fst_instance;
379  return ans;
380 }
381 
382 
383 void GrammarFst::Write(std::ostream &os, bool binary) const {
384  using namespace kaldi;
385  if (!binary)
386  KALDI_ERR << "GrammarFst::Write only supports binary mode.";
387  int32 format = 1,
388  num_ifsts = ifsts_.size();
389  WriteToken(os, binary, "<GrammarFst>");
390  WriteBasicType(os, binary, format);
391  WriteBasicType(os, binary, num_ifsts);
392  WriteBasicType(os, binary, nonterm_phones_offset_);
393 
394  std::string stream_name("unknown");
395  FstWriteOptions wopts(stream_name);
396  top_fst_->Write(os, wopts);
397 
398  for (int32 i = 0; i < num_ifsts; i++) {
399  int32 nonterminal = ifsts_[i].first;
400  WriteBasicType(os, binary, nonterminal);
401  ifsts_[i].second->Write(os, wopts);
402  }
403  WriteToken(os, binary, "</GrammarFst>");
404 }
405 
406 static ConstFst<StdArc> *ReadConstFstFromStream(std::istream &is) {
407  fst::FstHeader hdr;
408  std::string stream_name("unknown");
409  if (!hdr.Read(is, stream_name))
410  KALDI_ERR << "Reading FST: error reading FST header";
411  FstReadOptions ropts("<unspecified>", &hdr);
412  ConstFst<StdArc> *ans = ConstFst<StdArc>::Read(is, ropts);
413  if (!ans)
414  KALDI_ERR << "Could not read ConstFst from stream.";
415  return ans;
416 }
417 
418 
419 
420 void GrammarFst::Read(std::istream &is, bool binary) {
421  using namespace kaldi;
422  if (!binary)
423  KALDI_ERR << "GrammarFst::Read only supports binary mode.";
424  if (top_fst_ != NULL)
425  Destroy();
426  int32 format = 1, num_ifsts;
427  ExpectToken(is, binary, "<GrammarFst>");
428  ReadBasicType(is, binary, &format);
429  if (format != 1)
430  KALDI_ERR << "This version of the code cannot read this GrammarFst, "
431  "update your code.";
432  ReadBasicType(is, binary, &num_ifsts);
433  ReadBasicType(is, binary, &nonterm_phones_offset_);
434  top_fst_ = std::shared_ptr<const ConstFst<StdArc> >(ReadConstFstFromStream(is));
435  for (int32 i = 0; i < num_ifsts; i++) {
436  int32 nonterminal;
437  ReadBasicType(is, binary, &nonterminal);
438  std::shared_ptr<const ConstFst<StdArc> >
439  this_fst(ReadConstFstFromStream(is));
440  ifsts_.push_back(std::pair<int32, std::shared_ptr<const ConstFst<StdArc> > >(
441  nonterminal, this_fst));
442  }
443  Init();
444 }
445 
446 
473  VectorFst<StdArc> *fst) {
474  bool was_input_deterministic = true;
475  typedef StdArc Arc;
476  typedef Arc::StateId StateId;
477  typedef Arc::Label Label;
478  typedef Arc::Weight Weight;
479 
480  struct InfoForIlabel {
481  std::vector<size_t> arc_indexes; // indexes of all arcs with this ilabel
482  float tot_cost; // total cost of all arcs leaving state s for this
483  // ilabel, summed as if they were negative log-probs.
484  StateId new_state; // state-id of new state, if any, that we have created
485  // to remove duplicate symbols with this ilabel.
486  InfoForIlabel(): new_state(-1) { }
487  };
488 
489  std::unordered_map<Label, InfoForIlabel> label_map;
490 
491  size_t arc_index = 0;
492  for (ArcIterator<VectorFst<Arc> > aiter(*fst, s);
493  !aiter.Done(); aiter.Next(), ++arc_index) {
494  const Arc &arc = aiter.Value();
495  InfoForIlabel &info = label_map[arc.ilabel];
496  if (info.arc_indexes.empty()) {
497  info.tot_cost = arc.weight.Value();
498  } else {
499  info.tot_cost = -kaldi::LogAdd(-info.tot_cost, -arc.weight.Value());
500  was_input_deterministic = false;
501  }
502  info.arc_indexes.push_back(arc_index);
503  }
504 
505  if (was_input_deterministic)
506  return; // Nothing to do.
507 
508  // 'new_arcs' will contain the modified list of arcs
509  // leaving state s
510  std::vector<Arc> new_arcs;
511  new_arcs.reserve(arc_index);
512  arc_index = 0;
513  for (ArcIterator<VectorFst<Arc> > aiter(*fst, s);
514  !aiter.Done(); aiter.Next(), ++arc_index) {
515  const Arc &arc = aiter.Value();
516  Label ilabel = arc.ilabel;
517  InfoForIlabel &info = label_map[ilabel];
518  if (info.arc_indexes.size() == 1) {
519  new_arcs.push_back(arc); // no changes needed
520  } else {
521  if (info.new_state < 0) {
522  info.new_state = fst->AddState();
523  // add arc from state 's' to newly created state.
524  new_arcs.push_back(Arc(ilabel, 0, Weight(info.tot_cost),
525  info.new_state));
526  }
527  // add arc from new state to original destination of this arc.
528  fst->AddArc(info.new_state, Arc(0, arc.olabel,
529  Weight(arc.weight.Value() - info.tot_cost),
530  arc.nextstate));
531  }
532  }
533  fst->DeleteArcs(s);
534  for (size_t i = 0; i < new_arcs.size(); i++)
535  fst->AddArc(s, new_arcs[i]);
536 }
537 
538 
539 // This class contains the implementation of the function
540 // PrepareForGrammarFst(), which is declared in grammar-fst.h.
542  public:
543  using FST = VectorFst<StdArc>;
544  using Arc = StdArc;
546  using Label = Arc::Label;
548 
549  GrammarFstPreparer(int32 nonterm_phones_offset,
550  VectorFst<StdArc> *fst):
551  nonterm_phones_offset_(nonterm_phones_offset),
552  fst_(fst), orig_num_states_(fst->NumStates()),
553  simple_final_state_(kNoStateId) { }
554 
555  void Prepare() {
556  if (fst_->Start() == kNoStateId) {
557  KALDI_ERR << "FST has no states.";
558  }
559  for (StateId s = 0; s < fst_->NumStates(); s++) {
560  if (IsSpecialState(s)) {
561  if (NeedEpsilons(s)) {
562  InsertEpsilonsForState(s);
563  // This state won't be treated as a 'special' state any more;
564  // all 'special' arcs (arcs with ilabels >= kNontermBigNumber)
565  // have been moved and now leave from newly created states that
566  // this state transitions to via epsilons arcs.
567  } else {
568  // OK, state s is a special state.
569  FixArcsToFinalStates(s);
570  MaybeAddFinalProbToState(s);
571  // The following ensures that the start-state of sub-FSTs only has
572  // a single arc per left-context phone (the graph-building recipe can
573  // end up creating more than one if there were disambiguation symbols,
574  // e.g. for langauge model backoff).
575  if (s == fst_->Start() && IsEntryState(s))
577  }
578  }
579  }
580  StateId num_new_states = fst_->NumStates() - orig_num_states_;
581  KALDI_LOG << "Added " << num_new_states << " new states while "
582  "preparing for grammar FST.";
583  }
584 
585  private:
586 
587  // Returns true if state 's' has at least one arc coming out of it with a
588  // special nonterminal-related ilabel on it (i.e. an ilabel >=
589  // kNontermBigNumber), and false otherwise.
590  bool IsSpecialState(StateId s) const;
591 
592  // This function verifies that state s does not currently have any
593  // final-prob (crashes if that fails); then, if the arcs leaving s have
594  // nonterminal symbols kNontermEnd or user-defined nonterminals (>=
595  // kNontermUserDefined), it adds a final-prob with cost given by
596  // KALDI_GRAMMAR_FST_SPECIAL_WEIGHT to the state.
597  //
598  // State s is required to be a 'special state', i.e. have special symbols on
599  // arcs leaving it, and the function assumes (since it will already
600  // have been checked) that the arcs leaving s, if there are more than
601  // one, all correspond to the same nonterminal symbol.
602  void MaybeAddFinalProbToState(StateId s);
603 
604 
605  // This function does some checking for 'special states', that they have
606  // certain expected properties, and also detects certain problematic
607  // conditions that we need to fix. It returns true if we need to
608  // modify this state (by adding input-epsilon arcs), and false otherwise.
609  bool NeedEpsilons(StateId s) const;
610 
611  // Returns true if state s (which is expected to be the start state, although we
612  // don't check this) has arcs with nonterminal symbols #nonterm_begin.
613  bool IsEntryState(StateId s) const;
614 
615  // Fixes any final-prob-related problems with this state. The problem we aim
616  // to fix is that there may be arcs with nonterminal symbol #nonterm_end which
617  // transition from this state to a state with non-unit final prob. This
618  // function assimilates that final-prob into the arc leaving from this state,
619  // by making the arc transition to a new state with unit final-prob, and
620  // incorporating the original final-prob into the arc's weight.
621  //
622  // The purpose of this is to keep the GrammarFst code simple.
623  //
624  // It would have been more efficient to do this in CheckProperties(), but
625  // doing it this way is clearer; and the extra time taken here will be tiny.
626  void FixArcsToFinalStates(StateId s);
627 
628 
629  // This struct represents a category of arcs that are allowed to leave from
630  // the same 'special state'. If a special state has arcs leaving it that
631  // are in more than one category, it will need to be split up into
632  // multiple states connected by epsilons.
633  //
634  // The 'nonterminal' and 'nextstate' have to do with ensuring that all
635  // arcs leaving a particular FST state transition to the same FST instance
636  // (which, in turn, helps to keep the ArcIterator code efficient).
637  //
638  // The 'olabel' has to do with ensuring that arcs with user-defined
639  // nonterminals or kNontermEnd have no olabels on them. This is a requirement
640  // of the CombineArcs() function of GrammarFst, because it needs to combine
641  // two olabels into one so we need to know that at least one of the olabels is
642  // always epsilon.
643  struct ArcCategory {
644  int32 nonterminal; // The nonterminal symbol #nontermXXX encoded into the ilabel,
645  // or 0 if the ilabel was <kNontermBigNumber.
646  StateId nextstate; // If 'nonterminal' is a user-defined nonterminal like
647  // #nonterm:foo,
648  // then the destination state of the arc, else kNoStateId (-1).
649  Label olabel; // If 'nonterminal' is #nonterm_end or is a user-defined
650  // nonterminal (e.g. #nonterm:foo), then the olabel on the
651  // arc; else, 0.
652  bool operator < (const ArcCategory &other) const {
653  if (nonterminal < other.nonterminal) return true;
654  else if (nonterminal > other.nonterminal) return false;
655  if (nextstate < other.nextstate) return true;
656  else if (nextstate > other.nextstate) return false;
657  return olabel < other.olabel;
658  }
659  };
660 
661  // This function, which is used in CheckProperties() and
662  // InsertEpsilonsForState(), works out the categrory of the arc; see
663  // documentation of struct ArcCategory for more details.
664  void GetCategoryOfArc(const Arc &arc,
665  ArcCategory *arc_category) const;
666 
667 
668  // This will be called for 'special states' that need to be split up.
669  // Non-special arcs leaving this state will stay here. For each
670  // category of special arcs (see ArcCategory for details), a new
671  // state will be created and those arcs will leave from that state
672  // instead; for each such state, an input-epsilon arc will leave this state
673  // for that state. For more details, see the code.
674  void InsertEpsilonsForState(StateId s);
675 
677  return nonterm_phones_offset_ + static_cast<int32>(n);
678  }
679 
681  VectorFst<StdArc> *fst_;
683  // If needed we may add a 'simple final state' to fst_, which has unit
684  // final-prob. This is used when we ensure that states with kNontermExit on
685  // them transition to a state with unit final-prob, so we don't need to
686  // look at the final-prob when expanding states.
688 };
689 
691  if (fst_->Final(s).Value() == KALDI_GRAMMAR_FST_SPECIAL_WEIGHT) {
692  // TODO: find a way to detect if it was a coincidence, or not make it an
693  // error, because in principle a user-defined grammar could contain this
694  // special cost.
695  KALDI_WARN << "It looks like you are calling PrepareForGrammarFst twice.";
696  }
697  for (ArcIterator<FST> aiter(*fst_, s ); !aiter.Done(); aiter.Next()) {
698  const Arc &arc = aiter.Value();
699  if (arc.ilabel >= kNontermBigNumber) // 1 million
700  return true;
701  }
702  return false;
703 }
704 
706  int32 big_number = kNontermBigNumber,
707  encoding_multiple = GetEncodingMultiple(nonterm_phones_offset_);
708 
709  for (ArcIterator<FST> aiter(*fst_, s ); !aiter.Done(); aiter.Next()) {
710  const Arc &arc = aiter.Value();
711  int32 nonterminal = (arc.ilabel - big_number) /
712  encoding_multiple;
713  // we check that at least one has label with nonterminal equal to #nonterm_begin...
714  // in fact they will all have this value if at least one does, and this was checked
715  // in NeedEpsilons().
716  if (nonterminal == GetPhoneSymbolFor(kNontermBegin))
717  return true;
718  }
719  return false;
720 }
721 
722 
724 
725  // See the documentation for GetCategoryOfArc() for explanation of what these are.
726  std::set<ArcCategory> categories;
727 
728  if (fst_->Final(s) != Weight::Zero()) {
729  // A state having a final-prob is considered the same as it having
730  // a non-nonterminal arc out of it.. this would be like a transition
731  // within the same FST.
732  ArcCategory category;
733  category.nonterminal = 0;
734  category.nextstate = kNoStateId;
735  category.olabel = 0;
736  categories.insert(category);
737  }
738 
739  int32 big_number = kNontermBigNumber,
740  encoding_multiple = GetEncodingMultiple(nonterm_phones_offset_);
741 
742  for (ArcIterator<FST> aiter(*fst_, s ); !aiter.Done(); aiter.Next()) {
743  const Arc &arc = aiter.Value();
744  ArcCategory category;
745  GetCategoryOfArc(arc, &category);
746  categories.insert(category);
747 
748  // the rest of this block is just checking.
749  int32 nonterminal = category.nonterminal;
750 
751  if (nonterminal >= GetPhoneSymbolFor(kNontermUserDefined)) {
752  // Check that the destination state of this arc has arcs with
753  // kNontermReenter on them. We'll separately check that such states
754  // don't have other types of arcs leaving them (search for
755  // kNontermReenter below), so it's sufficient to check the first arc.
756  ArcIterator<FST> next_aiter(*fst_, arc.nextstate);
757  if (next_aiter.Done())
758  KALDI_ERR << "Destination state of a user-defined nonterminal "
759  "has no arcs leaving it.";
760  const Arc &next_arc = next_aiter.Value();
761  int32 next_nonterminal = (next_arc.ilabel - big_number) /
762  encoding_multiple;
763  if (next_nonterminal != GetPhoneSymbolFor(kNontermReenter)) {
764  KALDI_ERR << "Expected arcs with user-defined nonterminals to be "
765  "followed by arcs with kNontermReenter.";
766  }
767  }
768  if (nonterminal == GetPhoneSymbolFor(kNontermBegin) &&
769  s != fst_->Start()) {
770  KALDI_ERR << "#nonterm_begin symbol is present but this is not the "
771  "first state. Did you do fstdeterminizestar while compiling?";
772  }
773  if (nonterminal == GetPhoneSymbolFor(kNontermEnd)) {
774  if (fst_->NumArcs(arc.nextstate) != 0 ||
775  fst_->Final(arc.nextstate) == Weight::Zero()) {
776  KALDI_ERR << "Arc with kNontermEnd is not the final arc.";
777  }
778  }
779  }
780  if (categories.size() > 1) {
781  // This state has arcs leading to multiple FST instances.
782  // Do some checking to see that there is nothing really unexpected in
783  // there.
784  for (std::set<ArcCategory>::const_iterator
785  iter = categories.begin();
786  iter != categories.end(); ++iter) {
787  int32 nonterminal = iter->nonterminal;
788  if (nonterminal == nonterm_phones_offset_ + kNontermBegin ||
789  nonterminal == nonterm_phones_offset_ + kNontermReenter)
790  // we don't expect any state which has symbols like (kNontermBegin:p1)
791  // on arcs coming out of it, to also have other types of symbol. The
792  // same goes for kNontermReenter.
793  KALDI_ERR << "We do not expect states with arcs of type "
794  "kNontermBegin/kNontermReenter coming out of them, to also have "
795  "other types of arc.";
796  }
797  }
798  // the first half of the || below relates to olabels on arcs with either
799  // user-defined nonterminals or #nonterm_end (which would become 'leaving_arc'
800  // in the CombineArcs() function of GrammarFst). That function does not allow
801  // nonzero olabels on 'leaving_arc', which would be a problem if the
802  // 'arriving' arc had nonzero olabels, so we solve this by introducing
803  // input-epsilon arcs and putting the olabels on them instead.
804  bool need_epsilons = (categories.size() == 1 &&
805  categories.begin()->olabel != 0) ||
806  categories.size() > 1;
807  return need_epsilons;
808 }
809 
811  int32 encoding_multiple = GetEncodingMultiple(nonterm_phones_offset_),
812  big_number = kNontermBigNumber;
813  for (MutableArcIterator<FST> aiter(fst_, s ); !aiter.Done(); aiter.Next()) {
814  Arc arc = aiter.Value();
815  if (arc.ilabel < big_number)
816  continue;
817  int32 nonterminal = (arc.ilabel - big_number) / encoding_multiple;
818  if (nonterminal == GetPhoneSymbolFor(kNontermEnd)) {
819  KALDI_ASSERT(fst_->NumArcs(arc.nextstate) == 0 &&
820  fst_->Final(arc.nextstate) != Weight::Zero());
821  if (fst_->Final(arc.nextstate) == Weight::One())
822  continue; // There is no problem to fix.
823  if (simple_final_state_ == kNoStateId) {
824  simple_final_state_ = fst_->AddState();
825  fst_->SetFinal(simple_final_state_, Weight::One());
826  }
827  arc.weight = Times(arc.weight, fst_->Final(arc.nextstate));
828  arc.nextstate = simple_final_state_;
829  aiter.SetValue(arc);
830  }
831  }
832 }
833 
835  if (fst_->Final(s) != Weight::Zero()) {
836  // Something went wrong and it will require some debugging. In Prepare(),
837  // if we detected that the special state had a nonzero final-prob, we
838  // would have inserted epsilons to remove it, so there may be a bug in
839  // this class's code.
840  KALDI_ERR << "State already final-prob.";
841  }
842  ArcIterator<FST> aiter(*fst_, s );
843  KALDI_ASSERT(!aiter.Done());
844  const Arc &arc = aiter.Value();
845  int32 encoding_multiple = GetEncodingMultiple(nonterm_phones_offset_),
846  big_number = kNontermBigNumber,
847  nonterminal = (arc.ilabel - big_number) / encoding_multiple;
848  KALDI_ASSERT(nonterminal >= GetPhoneSymbolFor(kNontermBegin));
849  if (nonterminal == GetPhoneSymbolFor(kNontermEnd) ||
850  nonterminal >= GetPhoneSymbolFor(kNontermUserDefined)) {
852  }
853 }
854 
856  const Arc &arc, ArcCategory *arc_category) const {
857  int32 encoding_multiple = GetEncodingMultiple(nonterm_phones_offset_),
858  big_number = kNontermBigNumber;
859 
860  int32 ilabel = arc.ilabel;
861  if (ilabel < big_number) {
862  arc_category->nonterminal = 0;
863  arc_category->nextstate = kNoStateId;
864  arc_category->olabel = 0;
865  } else {
866  int32 nonterminal = (ilabel - big_number) / encoding_multiple;
867  arc_category->nonterminal = nonterminal;
868  if (nonterminal <= nonterm_phones_offset_) {
869  KALDI_ERR << "Problem decoding nonterminal symbol "
870  "(wrong --nonterm-phones-offset option?), ilabel="
871  << ilabel;
872  }
873  if (nonterminal >= GetPhoneSymbolFor(kNontermUserDefined)) {
874  // This is a user-defined symbol.
875  arc_category->nextstate = arc.nextstate;
876  arc_category->olabel = arc.olabel;
877  } else {
878  arc_category->nextstate = kNoStateId;
879  if (nonterminal == GetPhoneSymbolFor(kNontermEnd))
880  arc_category->olabel = arc.olabel;
881  else
882  arc_category->olabel = 0;
883  }
884  }
885 }
886 
887 
889  // Maps from category of arc, to a pair:
890  // the StateId is the state corresponding to that category.
891  // the float is the cost on the arc leading to that state;
892  // we compute the value that corresponds to the sum of the probabilities
893  // of the leaving arcs, bearing in mind that p = exp(-cost).
894  // We don't insert the arc-category whose 'nonterminal' is 0 here (i.e. the
895  // category for normal arcs); those arcs stay at this state.
896  std::map<ArcCategory, std::pair<StateId, float> > category_to_state;
897 
898  // This loop sets up 'category_to_state'.
899  for (fst::ArcIterator<FST> aiter(*fst_, s); !aiter.Done(); aiter.Next()) {
900  const Arc &arc = aiter.Value();
901  ArcCategory category;
902  GetCategoryOfArc(arc, &category);
903  int32 nonterminal = category.nonterminal;
904  if (nonterminal == 0)
905  continue;
906  if (nonterminal == GetPhoneSymbolFor(kNontermBegin) ||
907  nonterminal == GetPhoneSymbolFor(kNontermReenter)) {
908  KALDI_ERR << "Something went wrong; did not expect to insert epsilons "
909  "for this type of state.";
910  }
911  auto iter = category_to_state.find(category);
912  if (iter == category_to_state.end()) {
913  StateId new_state = fst_->AddState();
914  float cost = arc.weight.Value();
915  category_to_state[category] = std::pair<StateId, float>(new_state, cost);
916  } else {
917  std::pair<StateId, float> &p = iter->second;
918  p.second = -kaldi::LogAdd(-p.second, -arc.weight.Value());
919  }
920  }
921 
922  KALDI_ASSERT(!category_to_state.empty()); // would be a code error.
923 
924  // 'arcs_from_this_state' is a place to put arcs that will put on this state
925  // after we delete all its existing arcs.
926  std::vector<Arc> arcs_from_this_state;
927  arcs_from_this_state.reserve(fst_->NumArcs(s) + category_to_state.size());
928 
929  // add arcs corresponding to transitions to the newly created states, to
930  // 'arcs_from_this_state'
931  for (std::map<ArcCategory, std::pair<StateId, float> >::const_iterator
932  iter = category_to_state.begin(); iter != category_to_state.end();
933  ++iter) {
934  const ArcCategory &category = iter->first;
935  StateId new_state = iter->second.first;
936  float cost = iter->second.second;
937  Arc arc;
938  arc.ilabel = 0;
939  arc.olabel = category.olabel;
940  arc.weight = Weight(cost);
941  arc.nextstate = new_state;
942  arcs_from_this_state.push_back(arc);
943  }
944 
945  // Now add to 'arcs_from_this_state', and to the newly created states,
946  // arcs corresponding to each of the arcs that were originally leaving
947  // this state.
948  for (fst::ArcIterator<FST> aiter(*fst_, s); !aiter.Done(); aiter.Next()) {
949  const Arc &arc = aiter.Value();
950  ArcCategory category;
951  GetCategoryOfArc(arc, &category);
952  int32 nonterminal = category.nonterminal;
953  if (nonterminal == 0) { // this arc remains unchanged; we'll put it back later.
954  arcs_from_this_state.push_back(arc);
955  continue;
956  }
957  auto iter = category_to_state.find(category);
958  KALDI_ASSERT(iter != category_to_state.end());
959  Arc new_arc;
960  new_arc.ilabel = arc.ilabel;
961  if (arc.olabel == category.olabel) {
962  new_arc.olabel = 0; // the olabel went on the epsilon-input arc.
963  } else {
964  KALDI_ASSERT(category.olabel == 0);
965  new_arc.olabel = arc.olabel;
966  }
967  StateId new_state = iter->second.first;
968  float epsilon_arc_cost = iter->second.second;
969  new_arc.weight = Weight(arc.weight.Value() - epsilon_arc_cost);
970  new_arc.nextstate = arc.nextstate;
971  fst_->AddArc(new_state, new_arc);
972  }
973 
974  fst_->DeleteArcs(s);
975  for (size_t i = 0; i < arcs_from_this_state.size(); i++) {
976  fst_->AddArc(s, arcs_from_this_state[i]);
977  }
978  // leave the final-prob on this state as it was before.
979 }
980 
981 
982 void PrepareForGrammarFst(int32 nonterm_phones_offset,
983  VectorFst<StdArc> *fst) {
984  GrammarFstPreparer p(nonterm_phones_offset, fst);
985  p.Prepare();
986 }
987 
988 void CopyToVectorFst(GrammarFst *grammar_fst,
989  VectorFst<StdArc> *vector_fst) {
990  typedef GrammarFstArc::StateId GrammarStateId; // int64
991  typedef StdArc::StateId StdStateId; // int
992  typedef StdArc::Label Label;
993  typedef StdArc::Weight Weight;
994 
995  std::vector<std::pair<GrammarStateId, StdStateId> > queue;
996  std::unordered_map<GrammarStateId, StdStateId> state_map;
997 
998  vector_fst->DeleteStates();
999  state_map[grammar_fst->Start()] = vector_fst->AddState(); // state 0.
1000  vector_fst->SetStart(0);
1001 
1002  queue.push_back(
1003  std::pair<GrammarStateId, StdStateId>(grammar_fst->Start(), 0));
1004 
1005  while (!queue.empty()) {
1006  std::pair<GrammarStateId, StdStateId> p = queue.back();
1007  queue.pop_back();
1008  GrammarStateId grammar_state = p.first;
1009  StdStateId std_state = p.second;
1010  vector_fst->SetFinal(std_state, grammar_fst->Final(grammar_state));
1011  ArcIterator<GrammarFst> aiter(*grammar_fst, grammar_state);
1012  for (; !aiter.Done(); aiter.Next()) {
1013  const GrammarFstArc &grammar_arc = aiter.Value();
1014  StdArc std_arc;
1015  std_arc.ilabel = grammar_arc.ilabel;
1016  std_arc.olabel = grammar_arc.olabel;
1017  std_arc.weight = grammar_arc.weight;
1018  GrammarStateId next_grammar_state = grammar_arc.nextstate;
1019  StdStateId next_std_state;
1020  std::unordered_map<GrammarStateId, StdStateId>::const_iterator
1021  state_iter = state_map.find(next_grammar_state);
1022  if (state_iter == state_map.end()) {
1023  next_std_state = vector_fst->AddState();
1024  state_map[next_grammar_state] = next_std_state;
1025  queue.push_back(std::pair<GrammarStateId, StdStateId>(
1026  next_grammar_state, next_std_state));
1027  } else {
1028  next_std_state = state_iter->second;
1029  }
1030  std_arc.nextstate = next_std_state;
1031  vector_fst->AddArc(std_state, std_arc);
1032  }
1033  }
1034 }
1035 
1036 
1037 
1038 } // end namespace fst
void Write(std::ostream &os, bool binary) const
Definition: grammar-fst.cc:383
fst::StdArc::StateId StateId
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
static ConstFst< StdArc > * ReadConstFstFromStream(std::istream &is)
Definition: grammar-fst.cc:406
std::unordered_map< BaseStateId, ExpandedState * > expanded_states
Definition: grammar-fst.h:413
const ConstFst< StdArc > * fst
Definition: grammar-fst.h:406
VectorFst< StdArc > FST
Definition: grammar-fst.cc:543
VectorFst< StdArc > * fst_
Definition: grammar-fst.cc:681
int32 GetPhoneSymbolFor(enum NonterminalValues n) const
Definition: grammar-fst.cc:676
bool IsEntryState(StateId s) const
Definition: grammar-fst.cc:705
void Read(std::istream &os, bool binary)
Definition: grammar-fst.cc:420
void InitEntryOrReentryArcs(const ConstFst< StdArc > &fst, int32 entry_state, int32 nonterminal_symbol, std::unordered_map< int32, int32 > *phone_to_arc)
Definition: grammar-fst.cc:133
std::vector< StdArc > arcs
Definition: grammar-fst.h:392
void ReadBasicType(std::istream &is, bool binary, T *t)
ReadBasicType is the name of the read function for bool, integer types, and floating-point types...
Definition: io-funcs-inl.h:55
For an extended explanation of the framework of which grammar-fsts are a part, please see Support for...
Definition: graph.dox:21
fst::StdArc StdArc
ExpandedState * ExpandState(int32 instance_id, BaseStateId state_id)
Definition: grammar-fst.cc:171
bool InitEntryArcs(int32 i)
Definition: grammar-fst.cc:113
kaldi::int32 int32
int32 GetChildInstanceId(int32 instance_id, int32 nonterminal, int32 state)
Definition: grammar-fst.cc:281
NonterminalValues
An anonymous enum to define some values for symbols used in our grammar-fst framework.
Arc::Label Label
Definition: grammar-fst.h:109
#define KALDI_GRAMMAR_FST_SPECIAL_WEIGHT
Definition: grammar-fst.h:67
void PrepareForGrammarFst(int32 nonterm_phones_offset, VectorFst< StdArc > *fst)
This function prepares &#39;ifst&#39; for use in GrammarFst: it ensures that it has the expected properties...
Definition: grammar-fst.cc:982
static void CombineArcs(const StdArc &leaving_arc, const StdArc &arriving_arc, float cost_correction, StdArc *arc)
Called while expanding states, this function combines information from two arcs: one leaving one sub-...
Definition: grammar-fst.cc:199
std::unordered_map< int32, int32 > parent_reentry_arcs
Definition: grammar-fst.h:452
StdArc::StateId BaseStateId
Definition: grammar-fst.h:107
LatticeWeightTpl< FloatType > Times(const LatticeWeightTpl< FloatType > &w1, const LatticeWeightTpl< FloatType > &w2)
const fst::Fst< fst::StdArc > & fst_
GrammarFst()
This constructor should only be used prior to calling Read().
Definition: grammar-fst.h:154
void InitNonterminalMap()
Definition: grammar-fst.cc:97
Represents an expanded state in an FstInstance.
Definition: grammar-fst.h:376
static void InputDeterminizeSingleState(StdArc::StateId s, VectorFst< StdArc > *fst)
This utility function input-determinizes a specified state s of the FST &#39;fst&#39;.
Definition: grammar-fst.cc:472
void ExpectToken(std::istream &is, bool binary, const char *token)
ExpectToken tries to read in the given token, and throws an exception on failure. ...
Definition: io-funcs.cc:191
struct rnnlm::@11::@12 n
void InitInstances()
Definition: grammar-fst.cc:124
bool NeedEpsilons(StateId s) const
Definition: grammar-fst.cc:723
#define KALDI_ERR
Definition: kaldi-error.h:147
ExpandedState * ExpandStateUserDefined(int32 instance_id, BaseStateId state_id)
Definition: grammar-fst.cc:324
void MaybeAddFinalProbToState(StateId s)
Definition: grammar-fst.cc:834
bool IsSpecialState(StateId s) const
Definition: grammar-fst.cc:690
GrammarFst is an FST that is &#39;stitched together&#39; from multiple FSTs, that can recursively incorporate...
Definition: grammar-fst.h:96
#define KALDI_WARN
Definition: kaldi-error.h:150
const Arc & Value() const
Definition: grammar-fst.h:560
void WriteToken(std::ostream &os, bool binary, const char *token)
The WriteToken functions are for writing nonempty sequences of non-space characters.
Definition: io-funcs.cc:134
fst::StdArc::Label Label
fst::StdArc::Weight Weight
bool operator<(const Int32Pair &a, const Int32Pair &b)
Definition: cu-matrixdim.h:83
GrammarFstPreparer(int32 nonterm_phones_offset, VectorFst< StdArc > *fst)
Definition: grammar-fst.cc:549
double LogAdd(double x, double y)
Definition: kaldi-math.h:184
void GetCategoryOfArc(const Arc &arc, ArcCategory *arc_category) const
Definition: grammar-fst.cc:855
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
Weight Final(StateId s) const
Definition: grammar-fst.h:171
void InsertEpsilonsForState(StateId s)
Definition: grammar-fst.cc:888
ExpandedState * ExpandStateEnd(int32 instance_id, BaseStateId state_id)
Definition: grammar-fst.cc:220
void WriteBasicType(std::ostream &os, bool binary, T t)
WriteBasicType is the name of the write function for bool, integer types, and floating-point types...
Definition: io-funcs-inl.h:34
This is the overridden template for class ArcIterator for GrammarFst.
Definition: grammar-fst.h:496
void CopyToVectorFst(GrammarFst *grammar_fst, VectorFst< StdArc > *vector_fst)
This function copies a GrammarFst to a VectorFst (intended mostly for testing and comparison purposes...
Definition: grammar-fst.cc:988
Arc::StateId StateId
#define KALDI_LOG
Definition: kaldi-error.h:153
int32 GetEncodingMultiple(int32 nonterm_phones_offset)
void FixArcsToFinalStates(StateId s)
Definition: grammar-fst.cc:810
StateId Start() const
Definition: grammar-fst.h:165
void DecodeSymbol(Label label, int32 *nonterminal_symbol, int32 *left_context_phone)
Decodes an ilabel into a pair (nonterminal, left_context_phone).
Definition: grammar-fst.cc:75