1 // replace-util.h
2 
3 
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 // Copyright 2005-2010 Google, Inc.
17 // Author: riley@google.com (Michael Riley)
18 //
19 
20 // \file
21 // Utility classes for the recursive replacement of Fsts (RTNs).
22 
23 #ifndef FST_LIB_REPLACE_UTIL_H__
24 #define FST_LIB_REPLACE_UTIL_H__
25 
26 #include <vector>
27 using std::vector;
28 #include <unordered_map>
29 using std::unordered_map;
30 using std::unordered_multimap;
31 #include <unordered_set>
32 using std::unordered_set;
33 using std::unordered_multiset;
34 #include <map>
35 
36 #include <fst/connect.h>
37 #include <fst/mutable-fst.h>
38 #include <fst/topsort.h>
39 
40 
41 namespace fst {
42 
43 // This specifies what labels to output on the call or return arc
44 // REPLACE_LABEL_NEITHER puts epsilon labels on both input and output
45 // REPLACE_LABEL_BOTH puts non-epsilon labels on both input and output
46 // REPLACE_LABEL_INPUT puts non-epsilon on input and epsilon on output
47 // REPLACE_LABEL_OUTPUT puts epsilon on input and non-epsilon on output
48 // Note that REPLACE_LABEL_INPUT and REPLACE_LABEL_OUTPUT produce transducers
49 enum ReplaceLabelType { REPLACE_LABEL_NEITHER = 1, REPLACE_LABEL_INPUT = 2,
50                         REPLACE_LABEL_OUTPUT = 3, REPLACE_LABEL_BOTH = 4 };
51 
52 // By default ReplaceUtil will copy the input label of the 'replace arc'.
53 // The call_label_type and return_label_type options specify how to manage
54 // the labels of the call arc and the return arc of the replace FST
55 template <class A>
56 struct ReplaceUtilOptions : CacheOptions {
57   int64 root;    // root rule for expansion
58   ReplaceLabelType call_label_type;  // how to label call arc
59   ReplaceLabelType return_label_type;  // how to label return arc
60   int64 return_label;  // specifies label to put on return arc
61 
ReplaceUtilOptionsReplaceUtilOptions62   ReplaceUtilOptions(int64 r, ReplaceLabelType call_label_type,
63                      ReplaceLabelType return_label_type, int64 return_label)
64       : root(r),
65         call_label_type(call_label_type),
66         return_label_type(return_label_type),
67         return_label(return_label) {}
68 
ReplaceUtilOptionsReplaceUtilOptions69   explicit ReplaceUtilOptions(int64 r)
70       : root(r),
71         call_label_type(REPLACE_LABEL_INPUT),
72         return_label_type(REPLACE_LABEL_NEITHER),
73         return_label(0) {}
74 
ReplaceUtilOptionsReplaceUtilOptions75   ReplaceUtilOptions(int64 r, bool epsilon_replace_arc)  // b/w compatibility
76       : root(r),
77         call_label_type((epsilon_replace_arc) ? REPLACE_LABEL_NEITHER :
78                         REPLACE_LABEL_INPUT),
79         return_label_type(REPLACE_LABEL_NEITHER),
80         return_label(0) {}
81 
ReplaceUtilOptionsReplaceUtilOptions82   ReplaceUtilOptions()
83       : root(kNoLabel),
84         call_label_type(REPLACE_LABEL_INPUT),
85         return_label_type(REPLACE_LABEL_NEITHER),
86         return_label(0) {}
87 };
88 
89 template <class Arc>
90 void Replace(const vector<pair<typename Arc::Label, const Fst<Arc>* > >&,
91              MutableFst<Arc> *, fst::ReplaceUtilOptions<Arc> opts);
92 
93 // Utility class for the recursive replacement of Fsts (RTNs). The
94 // user provides a set of Label, Fst pairs at construction. These are
95 // used by methods for testing cyclic dependencies and connectedness
96 // and doing RTN connection and specific Fst replacement by label or
97 // for various optimization properties. The modified results can be
98 // obtained with the GetFstPairs() or GetMutableFstPairs() methods.
99 template <class Arc>
100 class ReplaceUtil {
101  public:
102   typedef typename Arc::Label Label;
103   typedef typename Arc::Weight Weight;
104   typedef typename Arc::StateId StateId;
105 
106   typedef pair<Label, const Fst<Arc>*> FstPair;
107   typedef pair<Label, MutableFst<Arc>*> MutableFstPair;
108   typedef unordered_map<Label, Label> NonTerminalHash;
109 
110   // Constructs from mutable Fsts; Fst ownership given to ReplaceUtil.
111   ReplaceUtil(const vector<MutableFstPair> &fst_pairs,
112               const ReplaceUtilOptions<Arc> &opts);
113 
114   // Constructs from Fsts; Fst ownership retained by caller.
115   ReplaceUtil(const vector<FstPair> &fst_pairs,
116               const ReplaceUtilOptions<Arc> &opts);
117 
118   // Constructs from ReplaceFst internals; ownership retained by caller.
119   ReplaceUtil(const vector<const Fst<Arc> *> &fst_array,
120               const NonTerminalHash &nonterminal_hash,
121               const ReplaceUtilOptions<Arc> &opts);
122 
~ReplaceUtil()123   ~ReplaceUtil() {
124     for (Label i = 0; i < fst_array_.size(); ++i)
125       delete fst_array_[i];
126   }
127 
128   // True if the non-terminal dependencies are cyclic. Cyclic
129   // dependencies will result in an unexpandable replace fst.
CyclicDependencies()130   bool CyclicDependencies() const {
131     GetDependencies(false);
132     return depprops_ & kCyclic;
133   }
134 
135   // Returns true if no useless Fsts, states or transitions.
Connected()136   bool Connected() const {
137     GetDependencies(false);
138     uint64 props = kAccessible | kCoAccessible;
139     for (Label i = 0; i < fst_array_.size(); ++i) {
140       if (!fst_array_[i])
141         continue;
142       if (fst_array_[i]->Properties(props, true) != props || !depaccess_[i])
143         return false;
144     }
145     return true;
146   }
147 
148   // Removes useless Fsts, states and transitions.
149   void Connect();
150 
151   // Replaces Fsts specified by labels.
152   // Does nothing if there are cyclic dependencies.
153   void ReplaceLabels(const vector<Label> &labels);
154 
155   // Replaces Fsts that have at most 'nstates' states, 'narcs' arcs and
156   // 'nnonterm' non-terminals (updating in reverse dependency order).
157   // Does nothing if there are cyclic dependencies.
158   void ReplaceBySize(size_t nstates, size_t narcs, size_t nnonterms);
159 
160   // Replaces singleton Fsts.
161   // Does nothing if there are cyclic dependencies.
ReplaceTrivial()162   void ReplaceTrivial() { ReplaceBySize(2, 1, 1); }
163 
164   // Replaces non-terminals that have at most 'ninstances' instances
165   // (updating in dependency order).
166   // Does nothing if there are cyclic dependencies.
167   void ReplaceByInstances(size_t ninstances);
168 
169   // Replaces non-terminals that have only one instance.
170   // Does nothing if there are cyclic dependencies.
ReplaceUnique()171   void ReplaceUnique() { ReplaceByInstances(1); }
172 
173   // Returns Label, Fst pairs; Fst ownership retained by ReplaceUtil.
174   void GetFstPairs(vector<FstPair> *fst_pairs);
175 
176   // Returns Label, MutableFst pairs; Fst ownership given to caller.
177   void GetMutableFstPairs(vector<MutableFstPair> *mutable_fst_pairs);
178 
179  private:
180   // Per Fst statistics
181   struct ReplaceStats {
182     StateId nstates;    // # of states
183     StateId nfinal;     // # of final states
184     size_t narcs;       // # of arcs
185     Label nnonterms;    // # of non-terminals in Fst
186     size_t nref;        // # of non-terminal instances referring to this Fst
187 
188     // # of times that ith Fst references this Fst
189     map<Label, size_t> inref;
190     // # of times that this Fst references the ith Fst
191     map<Label, size_t> outref;
192 
ReplaceStatsReplaceStats193     ReplaceStats()
194         : nstates(0),
195           nfinal(0),
196           narcs(0),
197           nnonterms(0),
198           nref(0) {}
199   };
200 
201   // Check Mutable Fsts exist o.w. create them.
202   void CheckMutableFsts();
203 
204   // Computes the dependency graph of the replace Fsts.
205   // If 'stats' is true, dependency statistics computed as well.
206   void GetDependencies(bool stats) const;
207 
ClearDependencies()208   void ClearDependencies() const {
209     depfst_.DeleteStates();
210     stats_.clear();
211     depprops_ = 0;
212     have_stats_ = false;
213   }
214 
215   // Get topological order of dependencies. Returns false with cyclic input.
216   bool GetTopOrder(const Fst<Arc> &fst, vector<Label> *toporder) const;
217 
218   // Update statistics assuming that jth Fst will be replaced.
219   void UpdateStats(Label j);
220 
221   Label root_label_;                              // root non-terminal
222   Label root_fst_;                                // root Fst ID
223   ReplaceLabelType call_label_type_;              // see Replace()
224   ReplaceLabelType return_label_type_;            // see Replace()
225   int64 return_label_;                            // see Replace()
226   vector<const Fst<Arc> *> fst_array_;            // Fst per ID
227   vector<MutableFst<Arc> *> mutable_fst_array_;   // MutableFst per ID
228   vector<Label> nonterminal_array_;               // Fst ID to non-terminal
229   NonTerminalHash nonterminal_hash_;              // non-terminal to Fst ID
230   mutable VectorFst<Arc> depfst_;                 // Fst ID dependencies
231   mutable vector<bool> depaccess_;                // Fst ID accessibility
232   mutable uint64 depprops_;                       // dependency Fst props
233   mutable bool have_stats_;                       // have dependency statistics
234   mutable vector<ReplaceStats> stats_;            // Per Fst statistics
235   DISALLOW_COPY_AND_ASSIGN(ReplaceUtil);
236 };
237 
238 template <class Arc>
ReplaceUtil(const vector<MutableFstPair> & fst_pairs,const ReplaceUtilOptions<Arc> & opts)239 ReplaceUtil<Arc>::ReplaceUtil(
240     const vector<MutableFstPair> &fst_pairs,
241     const ReplaceUtilOptions<Arc> &opts)
242     : root_label_(opts.root),
243       call_label_type_(opts.call_label_type),
244       return_label_type_(opts.return_label_type),
245       return_label_(opts.return_label),
246       depprops_(0),
247       have_stats_(false) {
248   fst_array_.push_back(0);
249   mutable_fst_array_.push_back(0);
250   nonterminal_array_.push_back(kNoLabel);
251   for (Label i = 0; i < fst_pairs.size(); ++i) {
252     Label label = fst_pairs[i].first;
253     MutableFst<Arc> *fst = fst_pairs[i].second;
254     nonterminal_hash_[label] = fst_array_.size();
255     nonterminal_array_.push_back(label);
256     fst_array_.push_back(fst);
257     mutable_fst_array_.push_back(fst);
258   }
259   root_fst_ = nonterminal_hash_[root_label_];
260   if (!root_fst_)
261     FSTERROR() << "ReplaceUtil: no root FST for label: " << root_label_;
262 }
263 
264 template <class Arc>
ReplaceUtil(const vector<FstPair> & fst_pairs,const ReplaceUtilOptions<Arc> & opts)265 ReplaceUtil<Arc>::ReplaceUtil(
266     const vector<FstPair> &fst_pairs,
267     const ReplaceUtilOptions<Arc> &opts)
268     : root_label_(opts.root),
269       call_label_type_(opts.call_label_type),
270       return_label_type_(opts.return_label_type),
271       return_label_(opts.return_label),
272       depprops_(0),
273       have_stats_(false) {
274   fst_array_.push_back(0);
275   nonterminal_array_.push_back(kNoLabel);
276   for (Label i = 0; i < fst_pairs.size(); ++i) {
277     Label label = fst_pairs[i].first;
278     const Fst<Arc> *fst = fst_pairs[i].second;
279     nonterminal_hash_[label] = fst_array_.size();
280     nonterminal_array_.push_back(label);
281     fst_array_.push_back(fst->Copy());
282   }
283   root_fst_ = nonterminal_hash_[root_label_];
284   if (!root_fst_)
285     FSTERROR() << "ReplaceUtil: no root FST for label: " << root_label_;
286 }
287 
288 template <class Arc>
ReplaceUtil(const vector<const Fst<Arc> * > & fst_array,const NonTerminalHash & nonterminal_hash,const ReplaceUtilOptions<Arc> & opts)289 ReplaceUtil<Arc>::ReplaceUtil(
290     const vector<const Fst<Arc> *> &fst_array,
291     const NonTerminalHash &nonterminal_hash,
292     const ReplaceUtilOptions<Arc> &opts)
293     : root_fst_(opts.root),
294       call_label_type_(opts.call_label_type),
295       return_label_type_(opts.return_label_type),
296       return_label_(opts.return_label),
297       nonterminal_array_(fst_array.size()),
298       nonterminal_hash_(nonterminal_hash),
299       depprops_(0),
300       have_stats_(false) {
301   fst_array_.push_back(0);
302   for (Label i = 1; i < fst_array.size(); ++i)
303     fst_array_.push_back(fst_array[i]->Copy());
304   for (typename NonTerminalHash::const_iterator it =
305            nonterminal_hash.begin(); it != nonterminal_hash.end(); ++it)
306     nonterminal_array_[it->second] = it->first;
307   root_label_ = nonterminal_array_[root_fst_];
308 }
309 
310 template <class Arc>
GetDependencies(bool stats)311 void ReplaceUtil<Arc>::GetDependencies(bool stats) const {
312   if (depfst_.NumStates() > 0) {
313     if (stats && !have_stats_)
314       ClearDependencies();
315     else
316       return;
317   }
318 
319   have_stats_ = stats;
320   if (have_stats_)
321     stats_.reserve(fst_array_.size());
322 
323   for (Label i = 0; i < fst_array_.size(); ++i) {
324     depfst_.AddState();
325     depfst_.SetFinal(i, Weight::One());
326     if (have_stats_)
327       stats_.push_back(ReplaceStats());
328   }
329   depfst_.SetStart(root_fst_);
330 
331   // An arc from each state (representing the fst) to the
332   // state representing the fst being replaced
333   for (Label i = 0; i < fst_array_.size(); ++i) {
334     const Fst<Arc> *ifst = fst_array_[i];
335     if (!ifst)
336       continue;
337     for (StateIterator<Fst<Arc> > siter(*ifst); !siter.Done(); siter.Next()) {
338       StateId s = siter.Value();
339       if (have_stats_) {
340         ++stats_[i].nstates;
341         if (ifst->Final(s) != Weight::Zero())
342           ++stats_[i].nfinal;
343       }
344       for (ArcIterator<Fst<Arc> > aiter(*ifst, s);
345            !aiter.Done(); aiter.Next()) {
346         if (have_stats_)
347           ++stats_[i].narcs;
348         const Arc& arc = aiter.Value();
349 
350         typename NonTerminalHash::const_iterator it =
351             nonterminal_hash_.find(arc.olabel);
352         if (it != nonterminal_hash_.end()) {
353           Label j = it->second;
354           depfst_.AddArc(i, Arc(arc.olabel, arc.olabel, Weight::One(), j));
355           if (have_stats_) {
356             ++stats_[i].nnonterms;
357             ++stats_[j].nref;
358             ++stats_[j].inref[i];
359             ++stats_[i].outref[j];
360           }
361         }
362       }
363     }
364   }
365 
366   // Gets accessibility info
367   SccVisitor<Arc> scc_visitor(0, &depaccess_, 0, &depprops_);
368   DfsVisit(depfst_, &scc_visitor);
369 }
370 
371 template <class Arc>
UpdateStats(Label j)372 void ReplaceUtil<Arc>::UpdateStats(Label j) {
373   if (!have_stats_) {
374     FSTERROR() << "ReplaceUtil::UpdateStats: stats not available";
375     return;
376   }
377 
378   if (j == root_fst_)  // can't replace root
379     return;
380 
381   typedef typename map<Label, size_t>::iterator Iter;
382   for (Iter in = stats_[j].inref.begin();
383        in != stats_[j].inref.end();
384        ++in) {
385     Label i = in->first;
386     size_t ni = in->second;
387     stats_[i].nstates += stats_[j].nstates * ni;
388     stats_[i].narcs += (stats_[j].narcs + 1) * ni;  // narcs - 1 + 2 (eps)
389     stats_[i].nnonterms += (stats_[j].nnonterms - 1) * ni;
390     stats_[i].outref.erase(stats_[i].outref.find(j));
391     for (Iter out = stats_[j].outref.begin();
392          out != stats_[j].outref.end();
393          ++out) {
394       Label k = out->first;
395       size_t nk = out->second;
396       stats_[i].outref[k] += ni * nk;
397     }
398   }
399 
400   for (Iter out = stats_[j].outref.begin();
401        out != stats_[j].outref.end();
402        ++out) {
403     Label k = out->first;
404     size_t nk = out->second;
405     stats_[k].nref -= nk;
406     stats_[k].inref.erase(stats_[k].inref.find(j));
407     for (Iter in = stats_[j].inref.begin();
408          in != stats_[j].inref.end();
409          ++in) {
410       Label i = in->first;
411       size_t ni = in->second;
412       stats_[k].inref[i] += ni * nk;
413       stats_[k].nref += ni * nk;
414     }
415   }
416 }
417 
418 template <class Arc>
CheckMutableFsts()419 void ReplaceUtil<Arc>::CheckMutableFsts() {
420   if (mutable_fst_array_.size() == 0) {
421     for (Label i = 0; i < fst_array_.size(); ++i) {
422       if (!fst_array_[i]) {
423         mutable_fst_array_.push_back(0);
424       } else {
425         mutable_fst_array_.push_back(new VectorFst<Arc>(*fst_array_[i]));
426         delete fst_array_[i];
427         fst_array_[i] = mutable_fst_array_[i];
428       }
429     }
430   }
431 }
432 
433 template <class Arc>
Connect()434 void ReplaceUtil<Arc>::Connect() {
435   CheckMutableFsts();
436   uint64 props = kAccessible | kCoAccessible;
437   for (Label i = 0; i < mutable_fst_array_.size(); ++i) {
438     if (!mutable_fst_array_[i])
439       continue;
440     if (mutable_fst_array_[i]->Properties(props, false) != props)
441       fst::Connect(mutable_fst_array_[i]);
442   }
443   GetDependencies(false);
444   for (Label i = 0; i < mutable_fst_array_.size(); ++i) {
445     MutableFst<Arc> *fst = mutable_fst_array_[i];
446     if (fst && !depaccess_[i]) {
447       delete fst;
448       fst_array_[i] = 0;
449       mutable_fst_array_[i] = 0;
450     }
451   }
452   ClearDependencies();
453 }
454 
455 template <class Arc>
GetTopOrder(const Fst<Arc> & fst,vector<Label> * toporder)456 bool ReplaceUtil<Arc>::GetTopOrder(const Fst<Arc> &fst,
457                                    vector<Label> *toporder) const {
458   // Finds topological order of dependencies.
459   vector<StateId> order;
460   bool acyclic = false;
461 
462   TopOrderVisitor<Arc> top_order_visitor(&order, &acyclic);
463   DfsVisit(fst, &top_order_visitor);
464   if (!acyclic) {
465     LOG(WARNING) << "ReplaceUtil::GetTopOrder: Cyclical label dependencies";
466     return false;
467   }
468 
469   toporder->resize(order.size());
470   for (Label i = 0; i < order.size(); ++i)
471     (*toporder)[order[i]] = i;
472 
473   return true;
474 }
475 
476 template <class Arc>
ReplaceLabels(const vector<Label> & labels)477 void ReplaceUtil<Arc>::ReplaceLabels(const vector<Label> &labels) {
478   CheckMutableFsts();
479   unordered_set<Label> label_set;
480   for (Label i = 0; i < labels.size(); ++i)
481     if (labels[i] != root_label_)  // can't replace root
482       label_set.insert(labels[i]);
483 
484   // Finds Fst dependencies restricted to the labels requested.
485   GetDependencies(false);
486   VectorFst<Arc> pfst(depfst_);
487   for (StateId i = 0; i < pfst.NumStates(); ++i) {
488     vector<Arc> arcs;
489     for (ArcIterator< VectorFst<Arc> > aiter(pfst, i);
490          !aiter.Done(); aiter.Next()) {
491       const Arc &arc = aiter.Value();
492       Label label = nonterminal_array_[arc.nextstate];
493       if (label_set.count(label) > 0)
494         arcs.push_back(arc);
495     }
496     pfst.DeleteArcs(i);
497     for (size_t j = 0; j < arcs.size(); ++j)
498       pfst.AddArc(i, arcs[j]);
499   }
500 
501   vector<Label> toporder;
502   if (!GetTopOrder(pfst, &toporder)) {
503     ClearDependencies();
504     return;
505   }
506 
507   // Visits Fsts in reverse topological order of dependencies and
508   // performs replacements.
509   for (Label o = toporder.size() - 1; o >= 0;  --o) {
510     vector<FstPair> fst_pairs;
511     StateId s = toporder[o];
512     for (ArcIterator< VectorFst<Arc> > aiter(pfst, s);
513          !aiter.Done(); aiter.Next()) {
514       const Arc &arc = aiter.Value();
515       Label label = nonterminal_array_[arc.nextstate];
516       const Fst<Arc> *fst = fst_array_[arc.nextstate];
517       fst_pairs.push_back(make_pair(label, fst));
518     }
519     if (fst_pairs.empty())
520         continue;
521     Label label = nonterminal_array_[s];
522     const Fst<Arc> *fst = fst_array_[s];
523     fst_pairs.push_back(make_pair(label, fst));
524 
525     Replace(fst_pairs, mutable_fst_array_[s],
526             ReplaceUtilOptions<Arc>(label, call_label_type_,
527                                     return_label_type_, return_label_));
528   }
529   ClearDependencies();
530 }
531 
532 template <class Arc>
ReplaceBySize(size_t nstates,size_t narcs,size_t nnonterms)533 void ReplaceUtil<Arc>::ReplaceBySize(size_t nstates, size_t narcs,
534                                      size_t nnonterms) {
535   vector<Label> labels;
536   GetDependencies(true);
537 
538   vector<Label> toporder;
539   if (!GetTopOrder(depfst_, &toporder)) {
540     ClearDependencies();
541     return;
542   }
543 
544   for (Label o = toporder.size() - 1; o >= 0; --o) {
545     Label j = toporder[o];
546     if (stats_[j].nstates <= nstates &&
547         stats_[j].narcs <= narcs &&
548         stats_[j].nnonterms <= nnonterms) {
549       labels.push_back(nonterminal_array_[j]);
550       UpdateStats(j);
551     }
552   }
553   ReplaceLabels(labels);
554 }
555 
556 template <class Arc>
ReplaceByInstances(size_t ninstances)557 void ReplaceUtil<Arc>::ReplaceByInstances(size_t ninstances) {
558   vector<Label> labels;
559   GetDependencies(true);
560 
561   vector<Label> toporder;
562   if (!GetTopOrder(depfst_, &toporder)) {
563     ClearDependencies();
564     return;
565   }
566   for (Label o = 0; o < toporder.size(); ++o) {
567     Label j = toporder[o];
568     if (stats_[j].nref <= ninstances) {
569       labels.push_back(nonterminal_array_[j]);
570       UpdateStats(j);
571     }
572   }
573   ReplaceLabels(labels);
574 }
575 
576 template <class Arc>
GetFstPairs(vector<FstPair> * fst_pairs)577 void ReplaceUtil<Arc>::GetFstPairs(vector<FstPair> *fst_pairs) {
578   CheckMutableFsts();
579   fst_pairs->clear();
580   for (Label i = 0; i < fst_array_.size(); ++i) {
581     Label label = nonterminal_array_[i];
582     const Fst<Arc> *fst = fst_array_[i];
583     if (!fst)
584       continue;
585     fst_pairs->push_back(make_pair(label, fst));
586   }
587 }
588 
589 template <class Arc>
GetMutableFstPairs(vector<MutableFstPair> * mutable_fst_pairs)590 void ReplaceUtil<Arc>::GetMutableFstPairs(
591     vector<MutableFstPair> *mutable_fst_pairs) {
592   CheckMutableFsts();
593   mutable_fst_pairs->clear();
594   for (Label i = 0; i < mutable_fst_array_.size(); ++i) {
595     Label label = nonterminal_array_[i];
596     MutableFst<Arc> *fst = mutable_fst_array_[i];
597     if (!fst)
598       continue;
599     mutable_fst_pairs->push_back(make_pair(label, fst->Copy()));
600   }
601 }
602 
603 }  // namespace fst
604 
605 #endif  // FST_LIB_REPLACE_UTIL_H__
606