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.

591 lines
20 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. // Classes for filtering the composition matches, e.g. for correct epsilon
  19. // handling.
  20. #ifndef FST_COMPOSE_FILTER_H_
  21. #define FST_COMPOSE_FILTER_H_
  22. #include <cstddef>
  23. #include <cstdint>
  24. #include <memory>
  25. #include <fst/filter-state.h>
  26. #include <fst/fst-decl.h> // For optional argument declarations
  27. #include <fst/fst.h>
  28. #include <fst/matcher.h>
  29. #include <fst/properties.h>
  30. namespace fst {
  31. // Composition filters determine which matches are allowed to proceed. The
  32. // filter's state is represeted by the type ComposeFilter::FilterState.
  33. // The basic filters handle correct epsilon matching. Their interface is:
  34. //
  35. // template <class M1, class M2>
  36. // class ComposeFilter {
  37. // public:
  38. // using Matcher1 = ...;
  39. // using Matcher2 = ...;
  40. // using FST1 = typename M1::FST;
  41. // using FST2 = typename M2::FST;
  42. // using FilterState = ...;
  43. //
  44. // using Arc = typename FST1::Arc;
  45. // using StateId = typename Arc::StateId;
  46. // using Weight = typename Arc::Weight;
  47. //
  48. // // Required constructor.
  49. // ComposeFilter(const FST1 &fst1, const FST2 &fst2,
  50. // M1 *matcher1 = nullptr, M2 *matcher2 = nullptr);
  51. //
  52. // // If safe=true, the copy is thread-safe. See Fst<>::Copy()
  53. // // for further doc.
  54. // ComposeFilter(const ComposeFilter<M1, M2> &filter,
  55. // bool safe = false);
  56. //
  57. // // Return start state of filter.
  58. // FilterState Start() const;
  59. //
  60. // // Specifies current composition state.
  61. // void SetState(StateId s1, StateId s2, const FilterState &fs);
  62. //
  63. // // Apply filter at current composition state to these transitions. If an
  64. // // arc label to be matched is kNolabel, then that side does not consume a
  65. // // symbol. Returns the new filter state or, if disallowed,
  66. // // FilterState::NoState(). The filter is permitted to modify its inputs
  67. // // (e.g. for optimization reasons).
  68. // FilterState FilterArc(Arc *arc1, Arc *arc2) const;
  69. // // Apply filter at current composition state to these final weights
  70. // // (cf. superfinal transitions). The filter may modify its inputs
  71. // // (e.g. for optimization reasons).
  72. // void FilterFinal(Weight *w1, Weight *w2) const;
  73. //
  74. // // Return the respective matchers. Ownership stays with filter. These
  75. // // methods allow the filter to access and possibly modify the compositio
  76. // // matchers (useful, e.g., with lookahead).
  77. //
  78. // Matcher1 *GetMatcher1();
  79. //
  80. // Matcher2 *GetMatcher2();
  81. //
  82. // // This specifies how the filter affects the composition result properties.
  83. // It takes as argument the properties that would apply with a trivial
  84. // // composition filter.
  85. // uint64_t Properties(uint64_t props) const;
  86. // };
  87. //
  88. // This filter allows only exact matching of symbols from FST1 with on FST2;
  89. // e.g., no special interpretation of epsilons.
  90. template <class M1, class M2 /* = M1 */>
  91. class NullComposeFilter {
  92. public:
  93. using Matcher1 = M1;
  94. using Matcher2 = M2;
  95. using FST1 = typename M1::FST;
  96. using FST2 = typename M2::FST;
  97. using FilterState = TrivialFilterState;
  98. using Arc = typename FST1::Arc;
  99. using Label = typename Arc::Label;
  100. using StateId = typename Arc::StateId;
  101. using Weight = typename Arc::Weight;
  102. NullComposeFilter(const FST1 &fst1, const FST2 &fst2,
  103. Matcher1 *matcher1 = nullptr, Matcher2 *matcher2 = nullptr)
  104. : matcher1_(matcher1 ? matcher1 : new Matcher1(fst1, MATCH_OUTPUT)),
  105. matcher2_(matcher2 ? matcher2 : new Matcher2(fst2, MATCH_INPUT)),
  106. fst1_(matcher1_->GetFst()),
  107. fst2_(matcher2_->GetFst()) {}
  108. NullComposeFilter(const NullComposeFilter &filter, bool safe = false)
  109. : matcher1_(filter.matcher1_->Copy(safe)),
  110. matcher2_(filter.matcher2_->Copy(safe)),
  111. fst1_(matcher1_->GetFst()),
  112. fst2_(matcher2_->GetFst()) {}
  113. FilterState Start() const { return FilterState(true); }
  114. void SetState(StateId, StateId, const FilterState &) {}
  115. FilterState FilterArc(Arc *arc1, Arc *arc2) const {
  116. return (arc1->olabel == kNoLabel || arc2->ilabel == kNoLabel)
  117. ? FilterState::NoState()
  118. : FilterState(true);
  119. }
  120. void FilterFinal(Weight *, Weight *) const {}
  121. Matcher1 *GetMatcher1() { return matcher1_.get(); }
  122. Matcher2 *GetMatcher2() { return matcher2_.get(); }
  123. uint64_t Properties(uint64_t props) const { return props; }
  124. private:
  125. std::unique_ptr<Matcher1> matcher1_;
  126. std::unique_ptr<Matcher2> matcher2_;
  127. const FST1 &fst1_;
  128. const FST2 &fst2_;
  129. };
  130. // This filter allows all epsilon matches, potentially resulting in redundant
  131. // epsilon paths. The use of this filter gives correct results iff one of the
  132. // following conditions hold:
  133. //
  134. // (1) The semiring is idempotent,
  135. // (2) the first FST is output-epsilon free, or
  136. // (3) the second FST is input-epsilon free.
  137. //
  138. // For (1), redundant epsilon paths may be created but won't hurt correctness.
  139. // For (2) and (3), no redundant paths are created.
  140. template <class M1, class M2 /* = M1 */>
  141. class TrivialComposeFilter {
  142. public:
  143. using Matcher1 = M1;
  144. using Matcher2 = M2;
  145. using FST1 = typename M1::FST;
  146. using FST2 = typename M2::FST;
  147. using FilterState = TrivialFilterState;
  148. using Arc = typename FST1::Arc;
  149. using Label = typename Arc::Label;
  150. using StateId = typename Arc::StateId;
  151. using Weight = typename Arc::Weight;
  152. TrivialComposeFilter(const FST1 &fst1, const FST2 &fst2,
  153. Matcher1 *matcher1 = nullptr,
  154. Matcher2 *matcher2 = nullptr)
  155. : matcher1_(matcher1 ? matcher1 : new Matcher1(fst1, MATCH_OUTPUT)),
  156. matcher2_(matcher2 ? matcher2 : new Matcher2(fst2, MATCH_INPUT)),
  157. fst1_(matcher1_->GetFst()),
  158. fst2_(matcher2_->GetFst()) {}
  159. TrivialComposeFilter(const TrivialComposeFilter<Matcher1, Matcher2> &filter,
  160. bool safe = false)
  161. : matcher1_(filter.matcher1_->Copy(safe)),
  162. matcher2_(filter.matcher2_->Copy(safe)),
  163. fst1_(matcher1_->GetFst()),
  164. fst2_(matcher2_->GetFst()) {}
  165. FilterState Start() const { return FilterState(true); }
  166. void SetState(StateId, StateId, const FilterState &) {}
  167. FilterState FilterArc(Arc *, Arc *) const { return FilterState(true); }
  168. void FilterFinal(Weight *, Weight *) const {}
  169. Matcher1 *GetMatcher1() { return matcher1_.get(); }
  170. Matcher2 *GetMatcher2() { return matcher2_.get(); }
  171. uint64_t Properties(uint64_t props) const { return props; }
  172. private:
  173. std::unique_ptr<Matcher1> matcher1_;
  174. std::unique_ptr<Matcher2> matcher2_;
  175. const FST1 &fst1_;
  176. const FST2 &fst2_;
  177. };
  178. // This filter requires epsilons on FST1 to be read before epsilons on FST2.
  179. template <class M1, class M2 /* = M1 */>
  180. class SequenceComposeFilter {
  181. public:
  182. using Matcher1 = M1;
  183. using Matcher2 = M2;
  184. using FST1 = typename M1::FST;
  185. using FST2 = typename M2::FST;
  186. using FilterState = CharFilterState;
  187. using Arc = typename FST1::Arc;
  188. using Label = typename Arc::Label;
  189. using StateId = typename Arc::StateId;
  190. using Weight = typename Arc::Weight;
  191. SequenceComposeFilter(const FST1 &fst1, const FST2 &fst2,
  192. Matcher1 *matcher1 = nullptr,
  193. Matcher2 *matcher2 = nullptr)
  194. : matcher1_(matcher1 ? matcher1 : new Matcher1(fst1, MATCH_OUTPUT)),
  195. matcher2_(matcher2 ? matcher2 : new Matcher2(fst2, MATCH_INPUT)),
  196. fst1_(matcher1_->GetFst()),
  197. s1_(kNoStateId),
  198. s2_(kNoStateId),
  199. fs_(kNoStateId) {}
  200. SequenceComposeFilter(const SequenceComposeFilter<Matcher1, Matcher2> &filter,
  201. bool safe = false)
  202. : matcher1_(filter.matcher1_->Copy(safe)),
  203. matcher2_(filter.matcher2_->Copy(safe)),
  204. fst1_(matcher1_->GetFst()),
  205. s1_(kNoStateId),
  206. s2_(kNoStateId),
  207. fs_(kNoStateId) {}
  208. FilterState Start() const { return FilterState(0); }
  209. void SetState(StateId s1, StateId s2, const FilterState &fs) {
  210. if (s1_ == s1 && s2_ == s2 && fs == fs_) return;
  211. s1_ = s1;
  212. s2_ = s2;
  213. fs_ = fs;
  214. const auto na1 = internal::NumArcs(fst1_, s1);
  215. const auto ne1 = internal::NumOutputEpsilons(fst1_, s1);
  216. const bool fin1 = internal::Final(fst1_, s1) != Weight::Zero();
  217. alleps1_ = na1 == ne1 && !fin1;
  218. noeps1_ = ne1 == 0;
  219. }
  220. FilterState FilterArc(Arc *arc1, Arc *arc2) const {
  221. if (arc1->olabel == kNoLabel) {
  222. return alleps1_ ? FilterState::NoState()
  223. : noeps1_ ? FilterState(0)
  224. : FilterState(1);
  225. } else if (arc2->ilabel == kNoLabel) {
  226. return fs_ != FilterState(0) ? FilterState::NoState() : FilterState(0);
  227. } else {
  228. return arc1->olabel == 0 ? FilterState::NoState() : FilterState(0);
  229. }
  230. }
  231. void FilterFinal(Weight *, Weight *) const {}
  232. Matcher1 *GetMatcher1() { return matcher1_.get(); }
  233. Matcher2 *GetMatcher2() { return matcher2_.get(); }
  234. uint64_t Properties(uint64_t props) const { return props; }
  235. private:
  236. std::unique_ptr<Matcher1> matcher1_;
  237. std::unique_ptr<Matcher2> matcher2_;
  238. const FST1 &fst1_;
  239. StateId s1_; // Current fst1_ state.
  240. StateId s2_; // Current fst2_ state.
  241. FilterState fs_; // Current filter state.
  242. bool alleps1_; // Only epsilons (and non-final) leaving s1_?
  243. bool noeps1_; // No epsilons leaving s1_?
  244. };
  245. // This filter requires epsilons on FST2 to be read before epsilons on FST1.
  246. template <class M1, class M2 /* = M1 */>
  247. class AltSequenceComposeFilter {
  248. public:
  249. using Matcher1 = M1;
  250. using Matcher2 = M2;
  251. using FST1 = typename M1::FST;
  252. using FST2 = typename M2::FST;
  253. using FilterState = CharFilterState;
  254. using Arc = typename FST1::Arc;
  255. using Label = typename Arc::Label;
  256. using StateId = typename Arc::StateId;
  257. using Weight = typename Arc::Weight;
  258. AltSequenceComposeFilter(const FST1 &fst1, const FST2 &fst2,
  259. Matcher1 *matcher1 = nullptr,
  260. Matcher2 *matcher2 = nullptr)
  261. : matcher1_(matcher1 ? matcher1 : new Matcher1(fst1, MATCH_OUTPUT)),
  262. matcher2_(matcher2 ? matcher2 : new Matcher2(fst2, MATCH_INPUT)),
  263. fst2_(matcher2_->GetFst()),
  264. s1_(kNoStateId),
  265. s2_(kNoStateId),
  266. fs_(kNoStateId) {}
  267. AltSequenceComposeFilter(
  268. const AltSequenceComposeFilter<Matcher1, Matcher2> &filter,
  269. bool safe = false)
  270. : matcher1_(filter.matcher1_->Copy(safe)),
  271. matcher2_(filter.matcher2_->Copy(safe)),
  272. fst2_(matcher2_->GetFst()),
  273. s1_(kNoStateId),
  274. s2_(kNoStateId),
  275. fs_(kNoStateId) {}
  276. FilterState Start() const { return FilterState(0); }
  277. void SetState(StateId s1, StateId s2, const FilterState &fs) {
  278. if (s1_ == s1 && s2_ == s2 && fs == fs_) return;
  279. s1_ = s1;
  280. s2_ = s2;
  281. fs_ = fs;
  282. const auto na2 = internal::NumArcs(fst2_, s2);
  283. const auto ne2 = internal::NumInputEpsilons(fst2_, s2);
  284. const bool fin2 = internal::Final(fst2_, s2) != Weight::Zero();
  285. alleps2_ = na2 == ne2 && !fin2;
  286. noeps2_ = ne2 == 0;
  287. }
  288. FilterState FilterArc(Arc *arc1, Arc *arc2) const {
  289. if (arc2->ilabel == kNoLabel) {
  290. return alleps2_ ? FilterState::NoState()
  291. : noeps2_ ? FilterState(0)
  292. : FilterState(1);
  293. } else if (arc1->olabel == kNoLabel) {
  294. return fs_ == FilterState(1) ? FilterState::NoState() : FilterState(0);
  295. } else {
  296. return arc1->olabel == 0 ? FilterState::NoState() : FilterState(0);
  297. }
  298. }
  299. void FilterFinal(Weight *, Weight *) const {}
  300. Matcher1 *GetMatcher1() { return matcher1_.get(); }
  301. Matcher2 *GetMatcher2() { return matcher2_.get(); }
  302. uint64_t Properties(uint64_t props) const { return props; }
  303. private:
  304. std::unique_ptr<Matcher1> matcher1_;
  305. std::unique_ptr<Matcher2> matcher2_;
  306. const FST2 &fst2_;
  307. StateId s1_; // Current fst1_ state.
  308. StateId s2_; // Current fst2_ state.
  309. FilterState fs_; // Current filter state.
  310. bool alleps2_; // Only epsilons (and non-final) leaving s2_?
  311. bool noeps2_; // No epsilons leaving s2_?
  312. };
  313. // This filter requires epsilons on FST1 to be matched with epsilons on FST2
  314. // whenever possible. (Template arg default declared in fst-decl.h.)
  315. template <class M1, class M2 /* = M1 */>
  316. class MatchComposeFilter {
  317. public:
  318. using Matcher1 = M1;
  319. using Matcher2 = M2;
  320. using FST1 = typename M1::FST;
  321. using FST2 = typename M2::FST;
  322. using FilterState = CharFilterState;
  323. using Arc = typename FST1::Arc;
  324. using Label = typename Arc::Label;
  325. using StateId = typename Arc::StateId;
  326. using Weight = typename Arc::Weight;
  327. MatchComposeFilter(const FST1 &fst1, const FST2 &fst2,
  328. Matcher1 *matcher1 = nullptr, Matcher2 *matcher2 = nullptr)
  329. : matcher1_(matcher1 ? matcher1 : new Matcher1(fst1, MATCH_OUTPUT)),
  330. matcher2_(matcher2 ? matcher2 : new Matcher2(fst2, MATCH_INPUT)),
  331. fst1_(matcher1_->GetFst()),
  332. fst2_(matcher2_->GetFst()),
  333. s1_(kNoStateId),
  334. s2_(kNoStateId),
  335. fs_(kNoStateId) {}
  336. MatchComposeFilter(const MatchComposeFilter<Matcher1, Matcher2> &filter,
  337. bool safe = false)
  338. : matcher1_(filter.matcher1_->Copy(safe)),
  339. matcher2_(filter.matcher2_->Copy(safe)),
  340. fst1_(matcher1_->GetFst()),
  341. fst2_(matcher2_->GetFst()),
  342. s1_(kNoStateId),
  343. s2_(kNoStateId),
  344. fs_(kNoStateId) {}
  345. FilterState Start() const { return FilterState(0); }
  346. void SetState(StateId s1, StateId s2, const FilterState &fs) {
  347. if (s1_ == s1 && s2_ == s2 && fs == fs_) return;
  348. s1_ = s1;
  349. s2_ = s2;
  350. fs_ = fs;
  351. size_t na1 = internal::NumArcs(fst1_, s1);
  352. size_t ne1 = internal::NumOutputEpsilons(fst1_, s1);
  353. bool f1 = internal::Final(fst1_, s1) != Weight::Zero();
  354. alleps1_ = na1 == ne1 && !f1;
  355. noeps1_ = ne1 == 0;
  356. size_t na2 = internal::NumArcs(fst2_, s2);
  357. size_t ne2 = internal::NumInputEpsilons(fst2_, s2);
  358. bool f2 = internal::Final(fst2_, s2) != Weight::Zero();
  359. alleps2_ = na2 == ne2 && !f2;
  360. noeps2_ = ne2 == 0;
  361. }
  362. FilterState FilterArc(Arc *arc1, Arc *arc2) const {
  363. if (arc2->ilabel == kNoLabel) { // Epsilon in FST1.
  364. return fs_ == FilterState(0)
  365. ? (noeps2_
  366. ? FilterState(0)
  367. : (alleps2_ ? FilterState::NoState() : FilterState(1)))
  368. : (fs_ == FilterState(1) ? FilterState(1)
  369. : FilterState::NoState());
  370. } else if (arc1->olabel == kNoLabel) { // Epsilon in FST2.
  371. return fs_ == FilterState(0)
  372. ? (noeps1_
  373. ? FilterState(0)
  374. : (alleps1_ ? FilterState::NoState() : FilterState(2)))
  375. : (fs_ == FilterState(2) ? FilterState(2)
  376. : FilterState::NoState());
  377. } else if (arc1->olabel == 0) { // Epsilon in both.
  378. return fs_ == FilterState(0) ? FilterState(0) : FilterState::NoState();
  379. } else { // Both are non-epsilons.
  380. return FilterState(0);
  381. }
  382. }
  383. void FilterFinal(Weight *, Weight *) const {}
  384. Matcher1 *GetMatcher1() { return matcher1_.get(); }
  385. Matcher2 *GetMatcher2() { return matcher2_.get(); }
  386. uint64_t Properties(uint64_t props) const { return props; }
  387. private:
  388. std::unique_ptr<Matcher1> matcher1_;
  389. std::unique_ptr<Matcher2> matcher2_;
  390. const FST1 &fst1_;
  391. const FST2 &fst2_;
  392. StateId s1_; // Current fst1_ state.
  393. StateId s2_; // Current fst2_ state.
  394. FilterState fs_; // Current filter state ID.
  395. bool alleps1_; // Only epsilson (and non-final) leaving s1?
  396. bool alleps2_; // Only epsilons (and non-final) leaving s2?
  397. bool noeps1_; // No epsilons leaving s1?
  398. bool noeps2_; // No epsilons leaving s2?
  399. };
  400. // This filter disallows matching epsilons on FST1 with epsilons on FST2,
  401. // but allows all other matches, potentially resulting in redundant
  402. // epsilon paths. The use of this filter gives correct results iff one of the
  403. // following conditions hold:
  404. //
  405. // (1) The semiring is idempotent,
  406. // (2) the first FST is output-epsilon free, or
  407. // (3) the second FST is input-epsilon free.
  408. //
  409. // For (1), redundant epsilon paths may be created but won't hurt correctness.
  410. // For (2) and (3), no redundant paths are created.
  411. template <class M1, class M2 /* = M1 */>
  412. class NoMatchComposeFilter {
  413. public:
  414. using Matcher1 = M1;
  415. using Matcher2 = M2;
  416. using FST1 = typename M1::FST;
  417. using FST2 = typename M2::FST;
  418. using FilterState = TrivialFilterState;
  419. using Arc = typename FST1::Arc;
  420. using Label = typename Arc::Label;
  421. using StateId = typename Arc::StateId;
  422. using Weight = typename Arc::Weight;
  423. NoMatchComposeFilter(const FST1 &fst1, const FST2 &fst2,
  424. Matcher1 *matcher1 = nullptr,
  425. Matcher2 *matcher2 = nullptr)
  426. : matcher1_(matcher1 ? matcher1 : new Matcher1(fst1, MATCH_OUTPUT)),
  427. matcher2_(matcher2 ? matcher2 : new Matcher2(fst2, MATCH_INPUT)),
  428. fst1_(matcher1_->GetFst()),
  429. fst2_(matcher2_->GetFst()) {}
  430. NoMatchComposeFilter(const NoMatchComposeFilter<Matcher1, Matcher2> &filter,
  431. bool safe = false)
  432. : matcher1_(filter.matcher1_->Copy(safe)),
  433. matcher2_(filter.matcher2_->Copy(safe)),
  434. fst1_(matcher1_->GetFst()),
  435. fst2_(matcher2_->GetFst()) {}
  436. FilterState Start() const { return FilterState(true); }
  437. void SetState(StateId, StateId, const FilterState &) {}
  438. FilterState FilterArc(Arc *arc1, Arc *arc2) const {
  439. return FilterState(arc1->olabel != 0 || arc2->ilabel != 0);
  440. }
  441. void FilterFinal(Weight *, Weight *) const {}
  442. Matcher1 *GetMatcher1() { return matcher1_.get(); }
  443. Matcher2 *GetMatcher2() { return matcher2_.get(); }
  444. uint64_t Properties(uint64_t props) const { return props; }
  445. private:
  446. std::unique_ptr<Matcher1> matcher1_;
  447. std::unique_ptr<Matcher2> matcher2_;
  448. const FST1 &fst1_;
  449. const FST2 &fst2_;
  450. };
  451. // This filter works with the MultiEpsMatcher to determine if multi-epsilons are
  452. // preserved in the composition output (rather than rewritten as 0) and
  453. // ensures correct properties.
  454. template <class Filter>
  455. class MultiEpsFilter {
  456. public:
  457. using Matcher1 = typename Filter::Matcher1;
  458. using Matcher2 = typename Filter::Matcher2;
  459. using FST1 = typename Filter::FST1;
  460. using FST2 = typename Filter::FST2;
  461. using FilterState = typename Filter::FilterState;
  462. using Arc = typename Filter::Arc;
  463. using Label = typename Arc::Label;
  464. using StateId = typename Arc::StateId;
  465. using Weight = typename Arc::Weight;
  466. MultiEpsFilter(const FST1 &fst1, const FST2 &fst2,
  467. Matcher1 *matcher1 = nullptr, Matcher2 *matcher2 = nullptr,
  468. bool keep_multi_eps = false)
  469. : filter_(fst1, fst2, matcher1, matcher2),
  470. keep_multi_eps_(keep_multi_eps) {}
  471. MultiEpsFilter(const MultiEpsFilter &filter, bool safe = false)
  472. : filter_(filter.filter_, safe),
  473. keep_multi_eps_(filter.keep_multi_eps_) {}
  474. FilterState Start() const { return filter_.Start(); }
  475. void SetState(StateId s1, StateId s2, const FilterState &fs) {
  476. return filter_.SetState(s1, s2, fs);
  477. }
  478. FilterState FilterArc(Arc *arc1, Arc *arc2) const {
  479. const auto fs = filter_.FilterArc(arc1, arc2);
  480. if (keep_multi_eps_) {
  481. if (arc1->olabel == kNoLabel) arc1->ilabel = arc2->ilabel;
  482. if (arc2->ilabel == kNoLabel) arc2->olabel = arc1->olabel;
  483. }
  484. return fs;
  485. }
  486. void FilterFinal(Weight *w1, Weight *w2) const {
  487. return filter_.FilterFinal(w1, w2);
  488. }
  489. Matcher1 *GetMatcher1() { return filter_.GetMatcher1(); }
  490. Matcher2 *GetMatcher2() { return filter_.GetMatcher2(); }
  491. uint64_t Properties(uint64_t iprops) const {
  492. const auto oprops = filter_.Properties(iprops);
  493. return oprops & kILabelInvariantProperties & kOLabelInvariantProperties;
  494. }
  495. private:
  496. Filter filter_;
  497. bool keep_multi_eps_;
  498. };
  499. } // namespace fst
  500. #endif // FST_COMPOSE_FILTER_H_