fsttablecompose.cc
Go to the documentation of this file.
1 // fstbin/fsttablecompose.cc
2 
3 // Copyright 2009-2011 Microsoft Corporation
4 // 2013 Johns Hopkins University (author: Daniel Povey)
5 
6 // See ../../COPYING for clarification regarding multiple authors
7 //
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 //
12 // http://www.apache.org/licenses/LICENSE-2.0
13 //
14 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 // MERCHANTABLITY OR NON-INFRINGEMENT.
18 // See the Apache 2 License for the specific language governing permissions and
19 // limitations under the License.
20 
21 
22 #include "base/kaldi-common.h"
23 #include "util/common-utils.h"
24 #include "fst/fstlib.h"
25 #include "fstext/table-matcher.h"
26 #include "fstext/fstext-utils.h"
27 #include "fstext/kaldi-fst-io.h"
28 
29 
30 /*
31  cd ~/tmpdir
32  while true; do
33  fstrand | fstarcsort --sort_type=olabel > 1.fst; fstrand | fstarcsort > 2.fst
34  fstcompose 1.fst 2.fst > 3a.fst
35  fsttablecompose 1.fst 2.fst > 3b.fst
36  fstequivalent --random=true 3a.fst 3b.fst || echo "Test failed"
37  echo -n "."
38  done
39 
40 */
41 
42 int main(int argc, char *argv[]) {
43  try {
44  using namespace kaldi;
45  using namespace fst;
46  using kaldi::int32;
47  /*
48  fsttablecompose should always give equivalent results to compose,
49  but it is more efficient for certain kinds of inputs.
50  In particular, it is useful when, say, the left FST has states
51  that typically either have epsilon olabels, or
52  one transition out for each of the possible symbols (as the
53  olabel). The same with the input symbols of the right-hand FST
54  is possible.
55  */
56 
57  const char *usage =
58  "Composition algorithm [between two FSTs of standard type, in tropical\n"
59  "semiring] that is more efficient for certain cases-- in particular,\n"
60  "where one of the FSTs (the left one, if --match-side=left) has large\n"
61  "out-degree\n"
62  "\n"
63  "Usage: fsttablecompose (fst1-rxfilename|fst1-rspecifier) "
64  "(fst2-rxfilename|fst2-rspecifier) [(out-rxfilename|out-rspecifier)]\n";
65 
66  ParseOptions po(usage);
67 
69  std::string match_side = "left";
70  std::string compose_filter = "sequence";
71 
72  po.Register("connect", &opts.connect, "If true, trim FST before output.");
73  po.Register("match-side", &match_side, "Side of composition to do table "
74  "match, one of: \"left\" or \"right\".");
75  po.Register("compose-filter", &compose_filter, "Composition filter to use, "
76  "one of: \"alt_sequence\", \"auto\", \"match\", \"sequence\"");
77 
78  po.Read(argc, argv);
79 
80  if (match_side == "left") {
81  opts.table_match_type = MATCH_OUTPUT;
82  } else if (match_side == "right") {
83  opts.table_match_type = MATCH_INPUT;
84  } else {
85  KALDI_ERR << "Invalid match-side option: " << match_side;
86  }
87 
88  if (compose_filter == "alt_sequence") {
89  opts.filter_type = ALT_SEQUENCE_FILTER;
90  } else if (compose_filter == "auto") {
91  opts.filter_type = AUTO_FILTER;
92  } else if (compose_filter == "match") {
93  opts.filter_type = MATCH_FILTER;
94  } else if (compose_filter == "sequence") {
95  opts.filter_type = SEQUENCE_FILTER;
96  } else {
97  KALDI_ERR << "Invalid compose-filter option: " << compose_filter;
98  }
99 
100  if (po.NumArgs() < 2 || po.NumArgs() > 3) {
101  po.PrintUsage();
102  exit(1);
103  }
104 
105  std::string fst1_in_str = po.GetArg(1),
106  fst2_in_str = po.GetArg(2),
107  fst_out_str = po.GetOptArg(3);
108 
109 
110  // Note: the "table" in is_table_1 and similar variables has nothing
111  // to do with the "table" in "fsttablecompose"; is_table_1 relates to
112  // whether we are dealing with a single FST or a whole set of FSTs.
113  bool is_table_1 =
114  (ClassifyRspecifier(fst1_in_str, NULL, NULL) != kNoRspecifier),
115  is_table_2 =
116  (ClassifyRspecifier(fst2_in_str, NULL, NULL) != kNoRspecifier),
117  is_table_out =
118  (ClassifyWspecifier(fst_out_str, NULL, NULL, NULL) != kNoWspecifier);
119  if (is_table_out != (is_table_1 || is_table_2))
120  KALDI_ERR << "Incompatible combination of archives and files";
121 
122  if (!is_table_1 && !is_table_2) { // Only dealing with files...
123  VectorFst<StdArc> *fst1 = ReadFstKaldi(fst1_in_str);
124 
125  VectorFst<StdArc> *fst2 = ReadFstKaldi(fst2_in_str);
126 
127  // Checks if <fst1> is olabel sorted and <fst2> is ilabel sorted.
128  if (fst1->Properties(fst::kOLabelSorted, true) == 0) {
129  KALDI_WARN << "The first FST is not olabel sorted.";
130  }
131  if (fst2->Properties(fst::kILabelSorted, true) == 0) {
132  KALDI_WARN << "The second FST is not ilabel sorted.";
133  }
134 
135  VectorFst<StdArc> composed_fst;
136 
137  TableCompose(*fst1, *fst2, &composed_fst, opts);
138 
139  delete fst1;
140  delete fst2;
141 
142  WriteFstKaldi(composed_fst, fst_out_str);
143  return 0;
144  } else if (!is_table_1 && is_table_2
145  && opts.table_match_type == MATCH_OUTPUT) {
146  // second arg is an archive, and match-side=left (default).
147  TableComposeCache<Fst<StdArc> > cache(opts);
148  VectorFst<StdArc> *fst1 = ReadFstKaldi(fst1_in_str);
149  SequentialTableReader<VectorFstHolder> fst2_reader(fst2_in_str);
150  TableWriter<VectorFstHolder> fst_writer(fst_out_str);
151  int32 n_done = 0;
152 
153  // Checks if <fst1> is olabel sorted.
154  if (fst1->Properties(fst::kOLabelSorted, true) == 0) {
155  KALDI_WARN << "The first FST is not olabel sorted.";
156  }
157  for (; !fst2_reader.Done(); fst2_reader.Next(), n_done++) {
158  VectorFst<StdArc> fst2(fst2_reader.Value());
159  VectorFst<StdArc> fst_out;
160  TableCompose(*fst1, fst2, &fst_out, &cache);
161  fst_writer.Write(fst2_reader.Key(), fst_out);
162  }
163  KALDI_LOG << "Composed " << n_done << " FSTs.";
164  return (n_done != 0 ? 0 : 1);
165  } else if (is_table_1 && is_table_2) {
166  SequentialTableReader<VectorFstHolder> fst1_reader(fst1_in_str);
167  RandomAccessTableReader<VectorFstHolder> fst2_reader(fst2_in_str);
168  TableWriter<VectorFstHolder> fst_writer(fst_out_str);
169  int32 n_done = 0, n_err = 0;
170  for (; !fst1_reader.Done(); fst1_reader.Next()) {
171  std::string key = fst1_reader.Key();
172  if (!fst2_reader.HasKey(key)) {
173  KALDI_WARN << "No such key " << key << " in second table.";
174  n_err++;
175  } else {
176  const VectorFst<StdArc> &fst1(fst1_reader.Value()),
177  &fst2(fst2_reader.Value(key));
178  VectorFst<StdArc> result;
179  TableCompose(fst1, fst2, &result, opts);
180  if (result.NumStates() == 0) {
181  KALDI_WARN << "Empty output for key " << key;
182  n_err++;
183  } else {
184  fst_writer.Write(key, result);
185  n_done++;
186  }
187  }
188  }
189  KALDI_LOG << "Successfully composed " << n_done << " FSTs, errors or "
190  << "empty output on " << n_err;
191  } else {
192  KALDI_ERR << "The combination of tables/non-tables and match-type that you "
193  << "supplied is not currently supported. Either implement this, "
194  << "ask the maintainers to implement it, or call this program "
195  << "differently.";
196  }
197  } catch(const std::exception &e) {
198  std::cerr << e.what();
199  return -1;
200  }
201 }
202 
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
void TableCompose(const Fst< Arc > &ifst1, const Fst< Arc > &ifst2, MutableFst< Arc > *ofst, const TableComposeOptions &opts=TableComposeOptions())
For an extended explanation of the framework of which grammar-fsts are a part, please see Support for...
Definition: graph.dox:21
void PrintUsage(bool print_command_line=false)
Prints the usage documentation [provided in the constructor].
A templated class for writing objects to an archive or script file; see The Table concept...
Definition: kaldi-table.h:368
kaldi::int32 int32
void Write(const std::string &key, const T &value) const
void Register(const std::string &name, bool *ptr, const std::string &doc)
RspecifierType ClassifyRspecifier(const std::string &rspecifier, std::string *rxfilename, RspecifierOptions *opts)
Definition: kaldi-table.cc:225
Allows random access to a collection of objects in an archive or script file; see The Table concept...
Definition: kaldi-table.h:233
TableComposeCache lets us do multiple compositions while caching the same matcher.
The class ParseOptions is for parsing command-line options; see Parsing command-line options for more...
Definition: parse-options.h:36
const T & Value(const std::string &key)
A templated class for reading objects sequentially from an archive or script file; see The Table conc...
Definition: kaldi-table.h:287
int Read(int argc, const char *const *argv)
Parses the command line options and fills the ParseOptions-registered variables.
#define KALDI_ERR
Definition: kaldi-error.h:147
#define KALDI_WARN
Definition: kaldi-error.h:150
std::string GetArg(int param) const
Returns one of the positional parameters; 1-based indexing for argc/argv compatibility.
bool HasKey(const std::string &key)
WspecifierType ClassifyWspecifier(const std::string &wspecifier, std::string *archive_wxfilename, std::string *script_wxfilename, WspecifierOptions *opts)
Definition: kaldi-table.cc:135
int NumArgs() const
Number of positional parameters (c.f. argc-1).
void WriteFstKaldi(std::ostream &os, bool binary, const VectorFst< Arc > &t)
void ReadFstKaldi(std::istream &is, bool binary, VectorFst< Arc > *fst)
int main(int argc, char *argv[])
#define KALDI_LOG
Definition: kaldi-error.h:153
std::string GetOptArg(int param) const