You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

577 lines
21 KiB

  1. // Copyright 2005-2024 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the 'License');
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an 'AS IS' BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. // See www.openfst.org for extensive documentation on this weighted
  16. // finite-state transducer library.
  17. //
  18. // Functions and classes to minimize an FST.
  19. #ifndef FST_MINIMIZE_H_
  20. #define FST_MINIMIZE_H_
  21. #include <algorithm>
  22. #include <cmath>
  23. #include <cstddef>
  24. #include <map>
  25. #include <memory>
  26. #include <queue>
  27. #include <utility>
  28. #include <vector>
  29. #include <fst/log.h>
  30. #include <fst/arc-map.h>
  31. #include <fst/arc.h>
  32. #include <fst/arcsort.h>
  33. #include <fst/connect.h>
  34. #include <fst/dfs-visit.h>
  35. #include <fst/encode.h>
  36. #include <fst/expanded-fst.h>
  37. #include <fst/factor-weight.h>
  38. #include <fst/fst.h>
  39. #include <fst/mutable-fst.h>
  40. #include <fst/partition.h>
  41. #include <fst/properties.h>
  42. #include <fst/push.h>
  43. #include <fst/queue.h>
  44. #include <fst/reverse.h>
  45. #include <fst/reweight.h>
  46. #include <fst/shortest-distance.h>
  47. #include <fst/state-map.h>
  48. #include <fst/string-weight.h>
  49. #include <fst/symbol-table.h>
  50. #include <fst/util.h>
  51. #include <fst/vector-fst.h>
  52. #include <fst/weight.h>
  53. #include <unordered_map>
  54. namespace fst {
  55. namespace internal {
  56. // Comparator for creating partition.
  57. template <class Arc>
  58. class StateComparator {
  59. public:
  60. using StateId = typename Arc::StateId;
  61. using Weight = typename Arc::Weight;
  62. StateComparator(const Fst<Arc> &fst, const Partition<StateId> &partition)
  63. : fst_(fst), partition_(partition) {}
  64. // Compares state x with state y based on sort criteria.
  65. bool operator()(const StateId x, const StateId y) const {
  66. // Checks for final state equivalence.
  67. const auto xfinal = fst_.Final(x).Hash();
  68. const auto yfinal = fst_.Final(y).Hash();
  69. if (xfinal < yfinal) {
  70. return true;
  71. } else if (xfinal > yfinal) {
  72. return false;
  73. }
  74. // Checks for number of arcs.
  75. if (fst_.NumArcs(x) < fst_.NumArcs(y)) return true;
  76. if (fst_.NumArcs(x) > fst_.NumArcs(y)) return false;
  77. // If the number of arcs are equal, checks for arc match.
  78. for (ArcIterator<Fst<Arc>> aiter1(fst_, x), aiter2(fst_, y);
  79. !aiter1.Done() && !aiter2.Done(); aiter1.Next(), aiter2.Next()) {
  80. const auto &arc1 = aiter1.Value();
  81. const auto &arc2 = aiter2.Value();
  82. if (arc1.ilabel < arc2.ilabel) return true;
  83. if (arc1.ilabel > arc2.ilabel) return false;
  84. if (partition_.ClassId(arc1.nextstate) <
  85. partition_.ClassId(arc2.nextstate))
  86. return true;
  87. if (partition_.ClassId(arc1.nextstate) >
  88. partition_.ClassId(arc2.nextstate))
  89. return false;
  90. }
  91. return false;
  92. }
  93. private:
  94. const Fst<Arc> &fst_;
  95. const Partition<StateId> &partition_;
  96. };
  97. // Computes equivalence classes for cyclic unweighted acceptors. For cyclic
  98. // minimization we use the classic Hopcroft minimization algorithm, which has
  99. // complexity O(E log V) where E is the number of arcs and V is the number of
  100. // states.
  101. //
  102. // For more information, see:
  103. //
  104. // Hopcroft, J. 1971. An n Log n algorithm for minimizing states in a finite
  105. // automaton. Ms, Stanford University.
  106. //
  107. // Note: the original presentation of the paper was for a finite automaton (==
  108. // deterministic, unweighted acceptor), but we also apply it to the
  109. // nondeterministic case, where it is also applicable as long as the semiring is
  110. // idempotent (if the semiring is not idempotent, there are some complexities
  111. // in keeping track of the weight when there are multiple arcs to states that
  112. // will be merged, and we don't deal with this).
  113. template <class Arc, class Queue>
  114. class CyclicMinimizer {
  115. public:
  116. using Label = typename Arc::Label;
  117. using StateId = typename Arc::StateId;
  118. using ClassId = typename Arc::StateId;
  119. using Weight = typename Arc::Weight;
  120. using RevArc = ReverseArc<Arc>;
  121. using RevArcIter = ArcIterator<Fst<RevArc>>;
  122. // TODO(wolfsonkin): Consider storing ArcIterator<> directly rather than in
  123. // unique_ptr when ArcIterator<Fst<>> is made movable.
  124. using RevArcIterPtr = std::unique_ptr<RevArcIter>;
  125. explicit CyclicMinimizer(const ExpandedFst<Arc> &fst) {
  126. Initialize(fst);
  127. Compute(fst);
  128. }
  129. const Partition<StateId> &GetPartition() const { return P_; }
  130. private:
  131. // StateILabelHasher is a hashing object that computes a hash-function
  132. // of an FST state that depends only on the set of ilabels on arcs leaving
  133. // the state [note: it assumes that the arcs are ilabel-sorted].
  134. // In order to work correctly for non-deterministic automata, multiple
  135. // instances of the same ilabel count the same as a single instance.
  136. class StateILabelHasher {
  137. public:
  138. explicit StateILabelHasher(const Fst<Arc> &fst) : fst_(fst) {}
  139. using Label = typename Arc::Label;
  140. using StateId = typename Arc::StateId;
  141. size_t operator()(const StateId s) {
  142. const size_t p1 = 7603;
  143. const size_t p2 = 433024223;
  144. size_t result = p2;
  145. size_t current_ilabel = kNoLabel;
  146. for (ArcIterator<Fst<Arc>> aiter(fst_, s); !aiter.Done(); aiter.Next()) {
  147. const Label this_ilabel = aiter.Value().ilabel;
  148. if (this_ilabel != current_ilabel) { // Ignores repeats.
  149. result = p1 * result + this_ilabel;
  150. current_ilabel = this_ilabel;
  151. }
  152. }
  153. return result;
  154. }
  155. private:
  156. const Fst<Arc> &fst_;
  157. };
  158. class ArcIterCompare {
  159. public:
  160. // Compares two iterators based on their input labels.
  161. bool operator()(const RevArcIterPtr &x, const RevArcIterPtr &y) const {
  162. const auto &xarc = x->Value();
  163. const auto &yarc = y->Value();
  164. return xarc.ilabel > yarc.ilabel;
  165. }
  166. };
  167. using ArcIterQueue =
  168. std::priority_queue<RevArcIterPtr, std::vector<RevArcIterPtr>,
  169. ArcIterCompare>;
  170. private:
  171. // Prepartitions the space into equivalence classes. We ensure that final and
  172. // non-final states always go into different equivalence classes, and we use
  173. // class StateILabelHasher to make sure that most of the time, states with
  174. // different sets of ilabels on arcs leaving them, go to different partitions.
  175. // Note: for the O(n) guarantees we don't rely on the goodness of this
  176. // hashing function---it just provides a bonus speedup.
  177. void PrePartition(const ExpandedFst<Arc> &fst) {
  178. VLOG(5) << "PrePartition";
  179. StateId next_class = 0;
  180. auto num_states = fst.NumStates();
  181. // Allocates a temporary vector to store the initial class mappings, so that
  182. // we can allocate the classes all at once.
  183. std::vector<StateId> state_to_initial_class(num_states);
  184. {
  185. // We maintain two maps from hash-value to class---one for final states
  186. // (final-prob == One()) and one for non-final states
  187. // (final-prob == Zero()). We are processing unweighted acceptors, so the
  188. // are the only two possible values.
  189. using HashToClassMap = std::unordered_map<size_t, StateId>;
  190. HashToClassMap hash_to_class_nonfinal;
  191. HashToClassMap hash_to_class_final;
  192. StateILabelHasher hasher(fst);
  193. for (StateId s = 0; s < num_states; ++s) {
  194. size_t hash = hasher(s);
  195. HashToClassMap &this_map =
  196. (fst.Final(s) != Weight::Zero() ? hash_to_class_final
  197. : hash_to_class_nonfinal);
  198. // Avoids two map lookups by using 'insert' instead of 'find'.
  199. auto p = this_map.emplace(hash, next_class);
  200. state_to_initial_class[s] = p.second ? next_class++ : p.first->second;
  201. }
  202. // Lets the maps go out of scope before we allocate the classes,
  203. // to reduce the maximum amount of memory used.
  204. }
  205. P_.AllocateClasses(next_class);
  206. for (StateId s = 0; s < num_states; ++s) {
  207. P_.Add(s, state_to_initial_class[s]);
  208. }
  209. for (StateId c = 0; c < next_class; ++c) L_.Enqueue(c);
  210. VLOG(5) << "Initial Partition: " << P_.NumClasses();
  211. }
  212. // Creates inverse transition Tr_ = rev(fst), loops over states in FST and
  213. // splits on final, creating two blocks in the partition corresponding to
  214. // final, non-final.
  215. void Initialize(const ExpandedFst<Arc> &fst) {
  216. // Constructs Tr.
  217. Reverse(fst, &Tr_);
  218. static const ILabelCompare<RevArc> icomp;
  219. ArcSort(&Tr_, icomp);
  220. // Tells the partition how many elements to allocate. The first state in
  221. // Tr_ is super-final state.
  222. P_.Initialize(Tr_.NumStates() - 1);
  223. // Prepares initial partition.
  224. PrePartition(fst);
  225. // Allocates arc iterator queue.
  226. aiter_queue_ = std::make_unique<ArcIterQueue>();
  227. }
  228. // Partitions all classes with destination C.
  229. void Split(ClassId C) {
  230. // Prepares priority queue: opens arc iterator for each state in C, and
  231. // inserts into priority queue.
  232. for (PartitionIterator<StateId> siter(P_, C); !siter.Done(); siter.Next()) {
  233. const auto s = siter.Value();
  234. if (Tr_.NumArcs(s + 1)) {
  235. aiter_queue_->push(std::make_unique<RevArcIter>(Tr_, s + 1));
  236. }
  237. }
  238. // Now pops arc iterator from queue, splits entering equivalence class, and
  239. // re-inserts updated iterator into queue.
  240. Label prev_label = -1;
  241. while (!aiter_queue_->empty()) {
  242. // NB: There is no way to "move" out of a std::priority_queue given that
  243. // the `top` accessor is a const ref. We const-cast to move out the
  244. // unique_ptr out of the priority queue. This is fine and doesn't cause an
  245. // issue with the invariants of the pqueue since we immediately pop after.
  246. RevArcIterPtr aiter =
  247. std::move(const_cast<RevArcIterPtr &>(aiter_queue_->top()));
  248. aiter_queue_->pop();
  249. if (aiter->Done()) continue;
  250. const auto &arc = aiter->Value();
  251. auto from_state = aiter->Value().nextstate - 1;
  252. auto from_label = arc.ilabel;
  253. if (prev_label != from_label) P_.FinalizeSplit(&L_);
  254. auto from_class = P_.ClassId(from_state);
  255. if (P_.ClassSize(from_class) > 1) P_.SplitOn(from_state);
  256. prev_label = from_label;
  257. aiter->Next();
  258. if (!aiter->Done()) aiter_queue_->push(std::move(aiter));
  259. }
  260. P_.FinalizeSplit(&L_);
  261. }
  262. // Main loop for Hopcroft minimization.
  263. void Compute(const Fst<Arc> &fst) {
  264. // Processes active classes (FIFO, or FILO).
  265. while (!L_.Empty()) {
  266. const auto C = L_.Head();
  267. L_.Dequeue();
  268. Split(C); // Splits on C, all labels in C.
  269. }
  270. }
  271. private:
  272. // Partioning of states into equivalence classes.
  273. Partition<StateId> P_;
  274. // Set of active classes to be processed in partition P.
  275. Queue L_;
  276. // Reverses transition function.
  277. VectorFst<RevArc> Tr_;
  278. // Priority queue of open arc iterators for all states in the splitter
  279. // equivalence class.
  280. std::unique_ptr<ArcIterQueue> aiter_queue_;
  281. };
  282. // Computes equivalence classes for acyclic FST.
  283. //
  284. // Complexity:
  285. //
  286. // O(E)
  287. //
  288. // where E is the number of arcs.
  289. //
  290. // For more information, see:
  291. //
  292. // Revuz, D. 1992. Minimization of acyclic deterministic automata in linear
  293. // time. Theoretical Computer Science 92(1): 181-189.
  294. template <class Arc>
  295. class AcyclicMinimizer {
  296. public:
  297. using Label = typename Arc::Label;
  298. using StateId = typename Arc::StateId;
  299. using ClassId = typename Arc::StateId;
  300. using Weight = typename Arc::Weight;
  301. explicit AcyclicMinimizer(const ExpandedFst<Arc> &fst) {
  302. Initialize(fst);
  303. Refine(fst);
  304. }
  305. const Partition<StateId> &GetPartition() { return partition_; }
  306. private:
  307. // DFS visitor to compute the height (distance) to final state.
  308. class HeightVisitor {
  309. public:
  310. HeightVisitor() : max_height_(0), num_states_(0) {}
  311. // Invoked before DFS visit.
  312. void InitVisit(const Fst<Arc> &fst) {}
  313. // Invoked when state is discovered (2nd arg is DFS tree root).
  314. bool InitState(StateId s, StateId root) {
  315. // Extends height array and initialize height (distance) to 0.
  316. for (StateId i = height_.size(); i <= s; ++i) height_.push_back(-1);
  317. if (s >= num_states_) num_states_ = s + 1;
  318. return true;
  319. }
  320. // Invoked when tree arc examined (to undiscovered state).
  321. bool TreeArc(StateId s, const Arc &arc) { return true; }
  322. // Invoked when back arc examined (to unfinished state).
  323. bool BackArc(StateId s, const Arc &arc) { return true; }
  324. // Invoked when forward or cross arc examined (to finished state).
  325. bool ForwardOrCrossArc(StateId s, const Arc &arc) {
  326. if (height_[arc.nextstate] + 1 > height_[s]) {
  327. height_[s] = height_[arc.nextstate] + 1;
  328. }
  329. return true;
  330. }
  331. // Invoked when state finished (parent is kNoStateId for tree root).
  332. void FinishState(StateId s, StateId parent, const Arc *parent_arc) {
  333. if (height_[s] == -1) height_[s] = 0;
  334. const auto h = height_[s] + 1;
  335. if (parent >= 0) {
  336. if (h > height_[parent]) height_[parent] = h;
  337. if (h > max_height_) max_height_ = h;
  338. }
  339. }
  340. // Invoked after DFS visit.
  341. void FinishVisit() {}
  342. size_t max_height() const { return max_height_; }
  343. const std::vector<StateId> &height() const { return height_; }
  344. size_t num_states() const { return num_states_; }
  345. private:
  346. std::vector<StateId> height_;
  347. size_t max_height_;
  348. size_t num_states_;
  349. };
  350. private:
  351. // Cluster states according to height (distance to final state)
  352. void Initialize(const Fst<Arc> &fst) {
  353. // Computes height (distance to final state).
  354. HeightVisitor hvisitor;
  355. DfsVisit(fst, &hvisitor);
  356. // Creates initial partition based on height.
  357. partition_.Initialize(hvisitor.num_states());
  358. partition_.AllocateClasses(hvisitor.max_height() + 1);
  359. const auto &hstates = hvisitor.height();
  360. for (StateId s = 0; s < hstates.size(); ++s) partition_.Add(s, hstates[s]);
  361. }
  362. // Refines states based on arc sort (out degree, arc equivalence).
  363. void Refine(const Fst<Arc> &fst) {
  364. using EquivalenceMap = std::map<StateId, StateId, StateComparator<Arc>>;
  365. StateComparator<Arc> comp(fst, partition_);
  366. // Starts with tail (height = 0).
  367. auto height = partition_.NumClasses();
  368. for (StateId h = 0; h < height; ++h) {
  369. EquivalenceMap equiv_classes(comp);
  370. // Sorts states within equivalence class.
  371. PartitionIterator<StateId> siter(partition_, h);
  372. equiv_classes[siter.Value()] = h;
  373. for (siter.Next(); !siter.Done(); siter.Next()) {
  374. auto insert_result = equiv_classes.emplace(siter.Value(), kNoStateId);
  375. if (insert_result.second) {
  376. insert_result.first->second = partition_.AddClass();
  377. }
  378. }
  379. // Creates refined partition.
  380. for (siter.Reset(); !siter.Done();) {
  381. const auto s = siter.Value();
  382. const auto old_class = partition_.ClassId(s);
  383. const auto new_class = equiv_classes[s];
  384. // A move operation can invalidate the iterator, so we first update
  385. // the iterator to the next element before we move the current element
  386. // out of the list.
  387. siter.Next();
  388. if (old_class != new_class) partition_.Move(s, new_class);
  389. }
  390. }
  391. }
  392. private:
  393. Partition<StateId> partition_;
  394. };
  395. // Given a partition and a Mutable FST, merges states of Fst in place (i.e.,
  396. // destructively). Merging works by taking the first state in a class of the
  397. // partition to be the representative state for the class. Each arc is then
  398. // reconnected to this state. All states in the class are merged by adding
  399. // their arcs to the representative state.
  400. template <class Arc>
  401. void MergeStates(const Partition<typename Arc::StateId> &partition,
  402. MutableFst<Arc> *fst) {
  403. using StateId = typename Arc::StateId;
  404. std::vector<StateId> state_map(partition.NumClasses());
  405. for (StateId i = 0; i < partition.NumClasses(); ++i) {
  406. PartitionIterator<StateId> siter(partition, i);
  407. state_map[i] = siter.Value(); // First state in partition.
  408. }
  409. // Relabels destination states.
  410. for (StateId c = 0; c < partition.NumClasses(); ++c) {
  411. for (PartitionIterator<StateId> siter(partition, c); !siter.Done();
  412. siter.Next()) {
  413. const auto s = siter.Value();
  414. for (MutableArcIterator<MutableFst<Arc>> aiter(fst, s); !aiter.Done();
  415. aiter.Next()) {
  416. auto arc = aiter.Value();
  417. arc.nextstate = state_map[partition.ClassId(arc.nextstate)];
  418. if (s == state_map[c]) { // For the first state, just sets destination.
  419. aiter.SetValue(arc);
  420. } else {
  421. fst->AddArc(state_map[c], std::move(arc));
  422. }
  423. }
  424. }
  425. }
  426. fst->SetStart(state_map[partition.ClassId(fst->Start())]);
  427. Connect(fst);
  428. }
  429. template <class Arc>
  430. void AcceptorMinimize(MutableFst<Arc> *fst) {
  431. // Connects FST before minimization, handles disconnected states.
  432. Connect(fst);
  433. if (fst->Start() == kNoStateId) return;
  434. // The Revuz acyclic algorithm won't work for nondeterministic inputs, so if
  435. // the input is nondeterministic, we force the use of the Hopcroft cyclic
  436. // algorithm instead.
  437. static constexpr auto revuz_props = kAcyclic | kIDeterministic;
  438. if (fst->Properties(revuz_props, true) == revuz_props) {
  439. // Acyclic minimization (Revuz).
  440. VLOG(2) << "Acyclic minimization";
  441. static const ILabelCompare<Arc> comp;
  442. ArcSort(fst, comp);
  443. AcyclicMinimizer<Arc> minimizer(*fst);
  444. MergeStates(minimizer.GetPartition(), fst);
  445. } else {
  446. // Either the FST has cycles, or it's generated from non-deterministic input
  447. // (which the Revuz algorithm can't handle), so use the cyclic minimization
  448. // algorithm of Hopcroft.
  449. VLOG(2) << "Cyclic minimization";
  450. CyclicMinimizer<Arc, LifoQueue<typename Arc::StateId>> minimizer(*fst);
  451. MergeStates(minimizer.GetPartition(), fst);
  452. }
  453. // Merges in appropriate semiring
  454. ArcUniqueMapper<Arc> mapper(*fst);
  455. StateMap(fst, mapper);
  456. }
  457. } // namespace internal
  458. // In place minimization of deterministic weighted automata and transducers, and
  459. // also non-deterministic ones if they use an idempotent semiring. For
  460. // transducers, if the 'sfst' argument is not null, the algorithm produces a
  461. // compact factorization of the minimal transducer.
  462. //
  463. // In the acyclic deterministic case, we use an algorithm from Revuz; this has
  464. // complexity O(e).
  465. //
  466. // In cyclic and non-deterministic cases, we use the classical Hopcroft
  467. // minimization (which was presented for the deterministic case but which
  468. // also works for non-deterministic FSTs); this has complexity O(e log v).
  469. template <class Arc>
  470. void Minimize(MutableFst<Arc> *fst, MutableFst<Arc> *sfst = nullptr,
  471. float delta = kShortestDelta, bool allow_nondet = false) {
  472. using Weight = typename Arc::Weight;
  473. static constexpr auto minimize_props =
  474. kAcceptor | kIDeterministic | kWeighted | kUnweighted;
  475. const auto props = fst->Properties(minimize_props, true);
  476. if (!(props & kIDeterministic)) {
  477. // Our approach to minimization of non-deterministic FSTs will only work in
  478. // idempotent semirings---for non-deterministic inputs, a state could have
  479. // multiple transitions to states that will get merged, and we'd have to
  480. // sum their weights. The algorithm doesn't handle that.
  481. if constexpr (!IsIdempotent<Weight>::value) {
  482. fst->SetProperties(kError, kError);
  483. FSTERROR() << "Cannot minimize a non-deterministic FST over a "
  484. "non-idempotent semiring";
  485. return;
  486. } else if (!allow_nondet) {
  487. fst->SetProperties(kError, kError);
  488. FSTERROR() << "Refusing to minimize a non-deterministic FST with "
  489. << "allow_nondet = false";
  490. return;
  491. }
  492. }
  493. if ((props & kAcceptor) != kAcceptor) { // Transducer.
  494. VectorFst<GallicArc<Arc, GALLIC_LEFT>> gfst;
  495. ArcMap(*fst, &gfst, ToGallicMapper<Arc, GALLIC_LEFT>());
  496. fst->DeleteStates();
  497. gfst.SetProperties(kAcceptor, kAcceptor);
  498. Push(&gfst, REWEIGHT_TO_INITIAL, delta);
  499. ArcMap(&gfst, QuantizeMapper<GallicArc<Arc, GALLIC_LEFT>>(delta));
  500. EncodeMapper<GallicArc<Arc, GALLIC_LEFT>> encoder(kEncodeLabels |
  501. kEncodeWeights);
  502. Encode(&gfst, &encoder);
  503. internal::AcceptorMinimize(&gfst);
  504. Decode(&gfst, encoder);
  505. if (!sfst) {
  506. FactorWeightFst<GallicArc<Arc, GALLIC_LEFT>,
  507. GallicFactor<typename Arc::Label, Weight, GALLIC_LEFT>>
  508. fwfst(gfst);
  509. std::unique_ptr<SymbolTable> osyms(
  510. fst->OutputSymbols() ? fst->OutputSymbols()->Copy() : nullptr);
  511. ArcMap(fwfst, fst, FromGallicMapper<Arc, GALLIC_LEFT>());
  512. fst->SetOutputSymbols(osyms.get());
  513. } else {
  514. sfst->SetOutputSymbols(fst->OutputSymbols());
  515. GallicToNewSymbolsMapper<Arc, GALLIC_LEFT> mapper(sfst);
  516. ArcMap(gfst, fst, &mapper);
  517. fst->SetOutputSymbols(sfst->InputSymbols());
  518. }
  519. } else if ((props & kWeighted) == kWeighted) { // Weighted acceptor.
  520. Push(fst, REWEIGHT_TO_INITIAL, delta);
  521. ArcMap(fst, QuantizeMapper<Arc>(delta));
  522. // We encode labels even though this is already an acceptor because weight
  523. // encoding gives us a transducer.
  524. EncodeMapper<Arc> encoder(kEncodeLabels | kEncodeWeights);
  525. Encode(fst, &encoder);
  526. internal::AcceptorMinimize(fst);
  527. Decode(fst, encoder);
  528. } else { // Unweighted acceptor.
  529. internal::AcceptorMinimize(fst);
  530. }
  531. }
  532. } // namespace fst
  533. #endif // FST_MINIMIZE_H_