1 // disambiguate.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 // \file
20 // Functions and classes to disambiguate an FST.
21 
22 #ifndef FST_LIB_DISAMBIGUATE_H__
23 #define FST_LIB_DISAMBIGUATE_H__
24 
25 #include <algorithm>
26 #include <climits>
27 #include <map>
28 #include <set>
29 #include <string>
30 #include <vector>
31 using std::vector;
32 
33 #include <fst/arcsort.h>
34 #include <fst/compose.h>
35 #include <fst/connect.h>
36 #include <fst/determinize.h>
37 #include <fst/dfs-visit.h>
38 #include <fst/project.h>
39 #include <fst/prune.h>
40 #include <fst/state-map.h>
41 #include <fst/state-table.h>
42 #include <fst/union-find.h>
43 #include <fst/verify.h>
44 
45 
46 namespace fst {
47 
48 template <class Arc>
49 struct DisambiguateOptions : public DeterminizeOptions<Arc> {
50   typedef typename Arc::StateId StateId;
51   typedef typename Arc::Weight Weight;
52   typedef typename Arc::Label Label;
53 
54   explicit DisambiguateOptions(float d = kDelta, Weight w = Weight::Zero(),
55                               StateId n = kNoStateId, Label l = 0)
56       : DeterminizeOptions<Arc>(d, w, n, l) {}
57 };
58 
59 // A determinize filter based on a subset element relation.
60 // The relation is assumed to be reflexive.
61 template <class Arc, class R>
62 class RelationDeterminizeFilter {
63  public:
64   typedef typename Arc::StateId StateId;
65   typedef typename Arc::Label Label;
66   typedef typename Arc::Weight Weight;
67 
68   typedef IntegerFilterState<StateId> FilterState;
69   typedef DeterminizeStateTuple<Arc, FilterState> StateTuple;
70   typedef typename StateTuple::Subset Subset;
71   typedef typename StateTuple::Element Element;
72   typedef multimap<Label, DeterminizeArc<StateTuple> > LabelMap;
73 
74   // This is needed e.g. to go into the gallic domain for transducers.
75   // No need to rebind the relation since its use here only depends
76   // on the state Ids.
77   template <class A>
78   struct rebind { typedef RelationDeterminizeFilter<A, R> other; };
79 
RelationDeterminizeFilter(const Fst<Arc> & fst)80   RelationDeterminizeFilter(const Fst<Arc> &fst)
81       : fst_(fst.Copy()),
82         r_(new R()),
83         s_(kNoStateId),
84         head_(0) {
85   }
86 
87   // Ownership of the relation is given to this class.
RelationDeterminizeFilter(const Fst<Arc> & fst,R * r)88   RelationDeterminizeFilter(const Fst<Arc> &fst, R *r)
89       : fst_(fst.Copy()),
90         r_(r),
91         s_(kNoStateId),
92         head_(0) {
93   }
94 
95   // Ownership of the relation is given to this class. Returns head states.
RelationDeterminizeFilter(const Fst<Arc> & fst,R * r,vector<StateId> * head)96   RelationDeterminizeFilter(const Fst<Arc> &fst, R *r, vector<StateId> *head)
97       : fst_(fst.Copy()),
98         r_(r),
99         s_(kNoStateId),
100         head_(head) {
101   }
102 
103   // This is needed e.g. to go into the gallic domain for transducers.
104   // Ownership of the templated filter argument is given to this class.
105   template <class F>
RelationDeterminizeFilter(const Fst<Arc> & fst,F * filter)106   RelationDeterminizeFilter(const Fst<Arc> &fst, F* filter)
107       : fst_(fst.Copy()),
108         r_(new R(filter->GetRelation())),
109         s_(kNoStateId),
110         head_(filter->GetHeadStates()) {
111     delete filter;
112   }
113 
114   // Copy ctr. The FST can be passed if it has been e.g. (deep) copied.
115   explicit RelationDeterminizeFilter(
116       const RelationDeterminizeFilter<Arc, R> &filter,
117       const Fst<Arc> *fst = 0)
118       : fst_(fst ? fst->Copy() : filter.fst_->Copy()),
119         r_(new R(*filter.r_)),
120         s_(kNoStateId),
121         head_() {
122   }
123 
~RelationDeterminizeFilter()124   ~RelationDeterminizeFilter() {
125     delete fst_;
126     delete r_;
127   }
128 
Start()129   FilterState Start() const { return FilterState(fst_->Start()); }
130 
SetState(StateId s,const StateTuple & tuple)131   void SetState(StateId s, const StateTuple &tuple) {
132     if (s_ != s) {
133       s_ = s;
134       tuple_ = &tuple;
135       StateId head = tuple.filter_state.GetState();
136       is_final_ = fst_->Final(head) != Weight::Zero();
137       if (head_) {
138         if (head_->size() <= s)
139           head_->resize(s + 1, kNoStateId);
140         (*head_)[s] = head;
141       }
142     }
143   }
144 
145   // Filters transition, possibly modifying label map. Returns
146   // true if arc is added to label map.
147   bool FilterArc(const Arc &arc,
148                  const Element &src_element,
149                  const Element &dest_element,
150                  LabelMap *label_map) const;
151 
152   // Filters super-final transition, returning new final weight
FilterFinal(const Weight final_weight,const Element & element)153   Weight FilterFinal(const Weight final_weight, const Element &element) const {
154     return is_final_ ? final_weight : Weight::Zero();
155   }
156 
Properties(uint64 props)157   static uint64 Properties(uint64 props) {
158     return props & ~(kIDeterministic | kODeterministic);
159   }
160 
GetRelation()161   const R &GetRelation() { return *r_; }
GetHeadStates()162   vector<StateId> *GetHeadStates() { return head_; }
163 
164  private:
165   // Pairs arc labels with state tuples with possible heads and
166   // empty subsets.
167   void InitLabelMap(LabelMap *label_map) const;
168 
169   Fst<Arc> *fst_;  // Input FST
170   R *r_;           // Relation compatible with the inverse trans. function
171   StateId s_;                    // Current state
172   const StateTuple *tuple_;      // Current tuple
173   bool is_final_;                // Is the current head state final?
174   vector<StateId> *head_;        // Head state for a given state.
175 
176   DISALLOW_COPY_AND_ASSIGN(RelationDeterminizeFilter);
177 };
178 
179 template <class Arc, class R> bool
FilterArc(const Arc & arc,const Element & src_element,const Element & dest_element,LabelMap * label_map)180 RelationDeterminizeFilter<Arc, R>::FilterArc(const Arc &arc,
181                                              const Element &src_element,
182                                              const Element &dest_element,
183                                              LabelMap *label_map) const {
184   bool added = false;
185   if (label_map->empty())
186     InitLabelMap(label_map);
187 
188   // Adds element to state tuple if element state is related to tuple head.
189   for (typename LabelMap::iterator liter = label_map->lower_bound(arc.ilabel);
190        liter != label_map->end() && liter->first == arc.ilabel;
191        ++liter) {
192     StateTuple *dest_tuple = liter->second.dest_tuple;
193     StateId dest_head = dest_tuple->filter_state.GetState();
194     if ((*r_)(dest_element.state_id, dest_head)) {
195       dest_tuple->subset.push_front(dest_element);
196       added = true;
197     }
198   }
199   return added;
200 }
201 
202 template <class Arc, class R> void
InitLabelMap(LabelMap * label_map)203 RelationDeterminizeFilter<Arc, R>::InitLabelMap(LabelMap *label_map) const {
204   StateId src_head = tuple_->filter_state.GetState();
205   Label label = kNoLabel;
206   StateId nextstate = kNoStateId;
207   for (ArcIterator< Fst<Arc> > aiter(*fst_, src_head);
208        !aiter.Done();
209        aiter.Next()) {
210     const Arc &arc = aiter.Value();
211     if (arc.ilabel == label && arc.nextstate == nextstate)
212       continue;  // multiarc
213     DeterminizeArc<StateTuple> det_arc(arc);
214     det_arc.dest_tuple->filter_state = FilterState(arc.nextstate);
215     pair<Label, DeterminizeArc<StateTuple> > pr(arc.ilabel, det_arc);
216     label_map->insert(pr);
217     label = arc.ilabel;
218     nextstate = arc.nextstate;
219   }
220 }
221 
222 // Helper class to disambiguate an FST via Disambiguate().
223 template <class Arc>
224 class Disambiguator {
225  public:
226   typedef typename Arc::StateId StateId;
227   typedef typename Arc::Weight Weight;
228   typedef typename Arc::Label Label;
229   // Ids arc with state id and arc position. Arc pos of -1 means
230   // final (super-final transition).
231   typedef pair<StateId, ssize_t> ArcId;
232 
Disambiguator()233   Disambiguator() : candidates_(0), merge_(0), error_(false) { }
234 
235   void Disambiguate(const Fst<Arc> &ifst, MutableFst<Arc> *ofst,
236                     const DisambiguateOptions<Arc> &opts =
237                     DisambiguateOptions<Arc>()) {
238     VectorFst<Arc> sfst(ifst);
239     ArcSort(&sfst, ArcCompare());
240     PreDisambiguate(sfst, ofst, opts);
241     ArcSort(ofst, ArcCompare());
242     FindAmbiguities(*ofst);
243     RemoveSplits(ofst);
244     MarkAmbiguities();
245     RemoveAmbiguities(ofst);
246     if (error_) ofst->SetProperties(kError, kError);
247   }
248 
249  private:
250   // Compare class for comparing input labels and next states of arcs.
251   // This sort order facilitates the predisambiguation.
252   class ArcCompare {
253    public:
operator()254     bool operator() (Arc arc1, Arc arc2) const {
255       return arc1.ilabel < arc2.ilabel ||
256           (arc1.ilabel == arc2.ilabel && arc1.nextstate < arc2.nextstate);
257     }
258 
Properties(uint64 props)259     uint64 Properties(uint64 props) const {
260       return (props & kArcSortProperties) | kILabelSorted |
261           (props & kAcceptor ? kOLabelSorted : 0);
262     }
263   };
264 
265   // Compare class for comparing transitions represented by their arc ID.
266   // This sort order facilitates ambiguity detection.
267   class ArcIdCompare {
268    public:
ArcIdCompare(const vector<StateId> & head)269     explicit ArcIdCompare(const vector<StateId> &head)
270         : head_(head) {
271     }
272 
operator()273     bool operator()(const ArcId &a1, const ArcId &a2) const {
274       StateId src1 = a1.first;
275       StateId src2 = a2.first;
276       StateId pos1 = a1.second;
277       StateId pos2 = a2.second;
278       StateId head1 = head_[src1];
279       StateId head2 = head_[src2];
280 
281       // Sort first by src head state
282       if (head1 < head2) return true;
283       if (head2 < head1) return false;
284 
285       // ... then by src state
286       if (src1 < src2) return true;
287       if (src2 < src1) return false;
288 
289       // ... then by position
290       return pos1 < pos2;
291     }
292 
293    private:
294     const vector<StateId> &head_;
295   };
296 
297   // A relation that determines if two states share a common future.
298   class CommonFuture {
299    public:
300     typedef GenericComposeStateTable<Arc, CharFilterState> T;
301     typedef typename T::StateTuple StateTuple;
302 
303     // Needed for compilation with DeterminizeRelationFilter
CommonFuture()304     CommonFuture() {
305       FSTERROR() << "Disambiguate::CommonFuture: FST not provided";
306     }
307 
CommonFuture(const Fst<Arc> & ifst)308     explicit CommonFuture(const Fst<Arc> &ifst) {
309       typedef Matcher< Fst<Arc> > M;
310       ComposeFstOptions<Arc, M, NullComposeFilter<M> > opts;
311 
312       // Ensures composition is between acceptors.
313       bool trans = ifst.Properties(kNotAcceptor, true);
314       const Fst<Arc> *fsa = trans ?
315           new ProjectFst<Arc>(ifst, PROJECT_INPUT) : &ifst;
316 
317       opts.state_table = new T(*fsa, *fsa);
318       ComposeFst<Arc> cfst(*fsa, *fsa, opts);
319       vector<bool> coaccess;
320       uint64 props = 0;
321       SccVisitor<Arc> scc_visitor(0, 0, &coaccess, &props);
322       DfsVisit(cfst, &scc_visitor);
323       for (StateId s = 0; s < coaccess.size(); ++s) {
324         if (coaccess[s]) {
325           const StateTuple &tuple = opts.state_table->Tuple(s);
326           related_.insert(tuple.StatePair());
327         }
328       }
329       if (trans)
330         delete fsa;
331     }
332 
operator()333     bool operator()(const StateId s1, StateId s2) const {
334       pair<StateId, StateId> pr(s1, s2);
335       return related_.count(pr) > 0;
336     }
337 
338    private:
339     // States s1 and s2 resp. are in this relation iff they there is a
340     // path from s1 to a final state that has the same label as some
341     // path from s2 to a final state.
342     set< pair<StateId, StateId> > related_;
343   };
344 
345   typedef multimap<ArcId, ArcId, ArcIdCompare> ArcIdMap;
346 
347   // Returns the arc corresponding to ArcId a
GetArc(const Fst<Arc> & fst,ArcId a)348   static Arc GetArc(const Fst<Arc> &fst, ArcId a) {
349     if (a.second == -1) {  // returns super-final transition
350       Arc arc(kNoLabel, kNoLabel, fst.Final(a.first), kNoStateId);
351       return arc;
352     } else {
353       ArcIterator< Fst<Arc> > aiter(fst, a.first);
354       aiter.Seek(a.second);
355       return aiter.Value();
356     }
357   }
358 
359   // Outputs an equivalent FST whose states are subsets of states
360   // that have a future path in common.
361   void PreDisambiguate(const ExpandedFst<Arc> &ifst, MutableFst<Arc> *ofst,
362                        const DisambiguateOptions<Arc> &opts);
363 
364   // Finds transitions that are ambiguous candidates in the result of
365   // PreDisambiguate.
366   void FindAmbiguities(const ExpandedFst<Arc> &fst);
367 
368   // Finds transition pairs that are ambiguous candidates from two specified
369   // source states.
370   void FindAmbiguousPairs(const ExpandedFst<Arc> &fst, StateId s1, StateId s2);
371 
372   // Marks ambiguous transitions to be removed.
373   void MarkAmbiguities();
374 
375   // Deletes spurious ambiguous transitions (due to quantization)
376   void RemoveSplits(MutableFst<Arc> *ofst);
377 
378   // Deletes actual ambiguous transitions.
379   void RemoveAmbiguities(MutableFst<Arc> *ofst);
380 
381   // States s1 and s2 are in this relation iff there is a path
382   // from the initial state to s1 that has the same label as some path
383   // from the initial state to s2.  We store only state pairs s1, s2
384   // such that s1 <= s2.
385   set< pair<StateId, StateId> > coreachable_;
386   // Queue of disambiguation-related states to be processed. We store
387   // only state pairs s1, s2 such that s1 <= s2.
388   list < pair<StateId, StateId> > queue_;
389   // Head state in the pre-disambiguation for a given state.
390   vector<StateId> head_;
391   // Maps from a candidate ambiguous arc A to each ambiguous candidate arc
392   // B with the same label and destination state as A, whose source state s'
393   // is coreachable with the source state s of A, and for which
394   // head(s') < head(s).
395   ArcIdMap *candidates_;
396   // Set of ambiguous transitions to be removed.
397   set<ArcId> ambiguous_;
398   // States to merge due to quantization issues.
399   UnionFind<StateId> *merge_;
400   // Marks error condition.
401   bool error_;
402   DISALLOW_COPY_AND_ASSIGN(Disambiguator);
403 };
404 
405 template <class Arc> void
PreDisambiguate(const ExpandedFst<Arc> & ifst,MutableFst<Arc> * ofst,const DisambiguateOptions<Arc> & opts)406 Disambiguator<Arc>::PreDisambiguate(const ExpandedFst<Arc> &ifst,
407                                     MutableFst<Arc> *ofst,
408                                     const DisambiguateOptions<Arc> &opts) {
409   typedef DefaultCommonDivisor<Weight> Div;
410   typedef RelationDeterminizeFilter<Arc, CommonFuture> Filt;
411 
412   // Subset elements with states s1 and s2 resp. are in this relation
413   // iff they there is a path from s1 to a final state that has the
414   // same label as some path from s2 to a final state.
415   CommonFuture *common_future = new CommonFuture(ifst);
416 
417   DeterminizeFstOptions<Arc, Div, Filt> nopts;
418   nopts.delta = opts.delta;
419   nopts.subsequential_label = opts.subsequential_label;
420   nopts.filter = new Filt(ifst, common_future, &head_);
421   nopts.gc_limit = 0;  // Cache only the last state for fastest copy.
422   if (opts.weight_threshold != Weight::Zero() ||
423       opts.state_threshold != kNoStateId) {
424     if (ifst.Properties(kAcceptor, true)) {
425       vector<Weight> idistance, odistance;
426       ShortestDistance(ifst, &idistance, true);
427       DeterminizeFst<Arc> dfst(ifst, &idistance, &odistance, nopts);
428       PruneOptions< Arc, AnyArcFilter<Arc> > popts(opts.weight_threshold,
429                                                    opts.state_threshold,
430                                                    AnyArcFilter<Arc>(),
431                                                    &odistance);
432       Prune(dfst, ofst, popts);
433     } else {
434       *ofst = DeterminizeFst<Arc>(ifst, nopts);
435       Prune(ofst, opts.weight_threshold, opts.state_threshold);
436     }
437   } else {
438     *ofst = DeterminizeFst<Arc>(ifst, nopts);
439   }
440   head_.resize(ofst->NumStates(), kNoStateId);
441 }
442 
443 template <class Arc> void
FindAmbiguities(const ExpandedFst<Arc> & fst)444 Disambiguator<Arc>::FindAmbiguities(const ExpandedFst<Arc> &fst) {
445   if (fst.Start() == kNoStateId)
446     return;
447 
448   candidates_ = new ArcIdMap(ArcIdCompare(head_));
449 
450   pair<StateId, StateId> start_pr(fst.Start(), fst.Start());
451   coreachable_.insert(start_pr);
452   queue_.push_back(start_pr);
453   while (!queue_.empty()) {
454     const pair<StateId, StateId>  &pr = queue_.front();
455     StateId s1 = pr.first;
456     StateId s2 = pr.second;
457     queue_.pop_front();
458     FindAmbiguousPairs(fst, s1, s2);
459   }
460 }
461 
462 template <class Arc>
FindAmbiguousPairs(const ExpandedFst<Arc> & fst,StateId s1,StateId s2)463 void Disambiguator<Arc>::FindAmbiguousPairs(const ExpandedFst<Arc> &fst,
464                                             StateId s1, StateId s2) {
465   if (fst.NumArcs(s2) > fst.NumArcs(s1))
466     FindAmbiguousPairs(fst, s2, s1);
467 
468   SortedMatcher < Fst<Arc> > matcher(fst, MATCH_INPUT);
469   matcher.SetState(s2);
470   for (ArcIterator< Fst<Arc> > aiter(fst, s1);
471        !aiter.Done();
472        aiter.Next()) {
473     const Arc &arc1 = aiter.Value();
474     ArcId a1(s1, aiter.Position());
475     if (matcher.Find(arc1.ilabel)) {
476       for (; !matcher.Done(); matcher.Next()) {
477         const Arc &arc2 = matcher.Value();
478         if (arc2.ilabel == kNoLabel)  // implicit epsilon match
479           continue;
480         ArcId a2(s2, matcher.Position());
481 
482         // Actual transition is ambiguous
483         if (s1 != s2 && arc1.nextstate == arc2.nextstate) {
484           pair<ArcId, ArcId> apr;
485           if (head_[s1] > head_[s2]) {
486             apr = make_pair(a1, a2);
487           } else {
488             apr = make_pair(a2, a1);
489           }
490           candidates_->insert(apr);
491         }
492 
493         pair<StateId, StateId> spr;
494         if (arc1.nextstate <= arc2.nextstate) {
495           spr = make_pair(arc1.nextstate, arc2.nextstate);
496         } else {
497           spr = make_pair(arc2.nextstate, arc1.nextstate);
498         }
499         // Not already marked as coreachable?
500         if (coreachable_.find(spr) == coreachable_.end()) {
501           coreachable_.insert(spr);
502           // Only possible if state split by quantization issues
503           if (spr.first != spr.second &&
504               head_[spr.first] == head_[spr.second]) {
505             if (!merge_) {
506               merge_ = new UnionFind<StateId>(fst.NumStates(), kNoStateId);
507               merge_->MakeAllSet(fst.NumStates());
508             }
509             merge_->Union(spr.first, spr.second);
510           } else {
511             queue_.push_back(spr);
512           }
513         }
514       }
515     }
516   }
517 
518   // Super-final transition is ambiguous
519   if (s1 != s2 &&
520       fst.Final(s1) != Weight::Zero() && fst.Final(s2) != Weight::Zero()) {
521     ArcId a1(s1, -1);
522     ArcId a2(s2, -1);
523     pair<ArcId, ArcId> apr;
524     if (head_[s1] > head_[s2]) {
525       apr = make_pair(a1, a2);
526     } else {
527       apr = make_pair(a2, a1);
528     }
529     candidates_->insert(apr);
530   }
531 }
532 
533 template <class Arc>
MarkAmbiguities()534 void Disambiguator<Arc>::MarkAmbiguities() {
535   if (!candidates_)
536     return;
537 
538   for (typename ArcIdMap::iterator it = candidates_->begin();
539        it != candidates_->end(); ++it) {
540     ArcId a = it->first;
541     ArcId b = it->second;
542     if (ambiguous_.count(b) == 0)  // if b is not to be removed
543       ambiguous_.insert(a);        // ... then a is.
544   }
545   coreachable_.clear();
546   delete candidates_;
547   candidates_ = 0;
548 }
549 
550 template <class Arc>
RemoveSplits(MutableFst<Arc> * ofst)551 void Disambiguator<Arc>::RemoveSplits(MutableFst<Arc> *ofst) {
552   if (!merge_)
553     return;
554 
555   // 'Merges' split states to remove spurious ambiguities
556   for (StateId s = 0; s < ofst->NumStates(); ++s) {
557     for (MutableArcIterator< MutableFst<Arc> > aiter(ofst, s);
558          !aiter.Done();
559          aiter.Next()) {
560       Arc arc = aiter.Value();
561       StateId nextstate = merge_->FindSet(arc.nextstate);
562       if (nextstate != arc.nextstate) {
563         arc.nextstate = nextstate;
564         aiter.SetValue(arc);
565       }
566     }
567   }
568 
569   // Repeats search for actual ambiguities on modified FST
570   coreachable_.clear();
571   delete merge_;
572   merge_ = 0;
573   delete candidates_;
574   candidates_ = 0;
575   FindAmbiguities(*ofst);
576   if (merge_) {  // shouldn't get here; sanity test
577     FSTERROR() << "Disambiguate: unable to remove spurious ambiguities";
578     error_ = true;
579     return;
580   }
581 }
582 
583 template <class Arc>
RemoveAmbiguities(MutableFst<Arc> * ofst)584 void Disambiguator<Arc>::RemoveAmbiguities(MutableFst<Arc> *ofst) {
585   if (ambiguous_.empty())
586     return;
587   // Adds dead state to redirect ambiguous transitions to be removed
588   StateId dead = ofst->AddState();
589   for (typename set<ArcId>::iterator it = ambiguous_.begin();
590        it != ambiguous_.end();
591        ++it) {
592     StateId s = it->first;
593     ssize_t pos = it->second;
594     if (pos >= 0) {  // actual transition
595       MutableArcIterator< MutableFst<Arc> > aiter(ofst, s);
596       aiter.Seek(pos);
597       Arc arc = aiter.Value();
598       arc.nextstate = dead;
599       aiter.SetValue(arc);
600     } else {        // super-final transition
601       ofst->SetFinal(s, Weight::Zero());
602     }
603   }
604   Connect(ofst);
605   ambiguous_.clear();
606 }
607 
608 // Disambiguates a weighted FST. This version writes the
609 // disambiguated FST to an output MutableFst. The result will
610 // be an equivalent FST that has the property that there are not
611 // two distinct paths from the initial state to a final state
612 // with the same input labeling.
613 //
614 // The weights must be (weakly) left divisible (valid for Tropical
615 // and LogWeight).
616 //
617 // Complexity:
618 // - Disambiguable: exponential (polynomial in the size of the output)
619 // - Non-disambiguable: does not terminate
620 //
621 // The disambiguable transducers include all automata and functional
622 // transducers that are unweighted or that are acyclic or that are unambiguous.
623 //
624 // References:
625 // - Mehryar Mohri. "A Disambiguation Algorithm for Finite Automata and
626 //   Functional Transducers". Proceedings CIAA 2012. Porto, Portugal, 2012.
627 template <class Arc>
628 void Disambiguate(const Fst<Arc> &ifst, MutableFst<Arc> *ofst,
629                   const DisambiguateOptions<Arc> &opts
630                  = DisambiguateOptions<Arc>()) {
631   Disambiguator<Arc> disamb;
632   disamb.Disambiguate(ifst, ofst, opts);
633 }
634 
635 }  // namespace fst
636 
637 #endif  // FST_LIB_DISAMBIGUATE_H__
638