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