FST  openfst-1.8.2
OpenFst Library
algo_test.h
Go to the documentation of this file.
1 // Copyright 2005-2020 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 // Regression test for various FST algorithms.
19 
20 #ifndef FST_TEST_ALGO_TEST_H_
21 #define FST_TEST_ALGO_TEST_H_
22 
23 #include <cstdint>
24 #include <memory>
25 #include <random>
26 #include <utility>
27 
28 #include <fst/log.h>
29 #include <fst/fstlib.h>
30 #include <fst/weight.h>
31 #include <fst/test/rand-fst.h>
32 
33 DECLARE_int32(repeat); // defined in ./algo_test.cc
34 
35 namespace fst {
36 
37 // Mapper to change input and output label of every transition into
38 // epsilons.
39 template <class A>
40 class EpsMapper {
41  public:
42  EpsMapper() {}
43 
44  A operator()(const A &arc) const {
45  return A(0, 0, arc.weight, arc.nextstate);
46  }
47 
48  uint64_t Properties(uint64_t props) const {
49  props &= ~kNotAcceptor;
50  props |= kAcceptor;
51  props &= ~kNoIEpsilons & ~kNoOEpsilons & ~kNoEpsilons;
52  props |= kIEpsilons | kOEpsilons | kEpsilons;
54  props |= kILabelSorted | kOLabelSorted;
55  return props;
56  }
57 
59 
61 
63 };
64 
65 // Generic - no lookahead.
66 template <class Arc>
67 void LookAheadCompose(const Fst<Arc> &ifst1, const Fst<Arc> &ifst2,
68  MutableFst<Arc> *ofst) {
69  Compose(ifst1, ifst2, ofst);
70 }
71 
72 // Specialized and epsilon olabel acyclic - lookahead.
73 inline void LookAheadCompose(const Fst<StdArc> &ifst1, const Fst<StdArc> &ifst2,
74  MutableFst<StdArc> *ofst) {
75  std::vector<StdArc::StateId> order;
76  bool acyclic;
77  TopOrderVisitor<StdArc> visitor(&order, &acyclic);
78  DfsVisit(ifst1, &visitor, OutputEpsilonArcFilter<StdArc>());
79  if (acyclic) { // no ifst1 output epsilon cycles?
80  StdOLabelLookAheadFst lfst1(ifst1);
81  StdVectorFst lfst2(ifst2);
82  LabelLookAheadRelabeler<StdArc>::Relabel(&lfst2, lfst1, true);
83  Compose(lfst1, lfst2, ofst);
84  } else {
85  Compose(ifst1, ifst2, ofst);
86  }
87 }
88 
89 // This class tests a variety of identities and properties that must
90 // hold for various algorithms on weighted FSTs.
91 template <class Arc>
93  public:
94  using Label = typename Arc::Label;
95  using StateId = typename Arc::StateId;
96  using Weight = typename Arc::Weight;
98 
99  WeightedTester(uint64_t seed, const Fst<Arc> &zero_fst,
100  const Fst<Arc> &one_fst, const Fst<Arc> &univ_fst,
101  WeightGenerator weight_generator)
102  : seed_(seed),
103  rand_(seed),
104  zero_fst_(zero_fst),
105  one_fst_(one_fst),
106  univ_fst_(univ_fst),
107  generate_(std::move(weight_generator)) {}
108 
109  void Test(const Fst<Arc> &T1, const Fst<Arc> &T2, const Fst<Arc> &T3) {
110  TestRational(T1, T2, T3);
111  TestMap(T1);
112  TestCompose(T1, T2, T3);
113  TestSort(T1);
114  TestOptimize(T1);
115  TestSearch(T1);
116  }
117 
118  private:
119  // Tests rational operations with identities
120  void TestRational(const Fst<Arc> &T1, const Fst<Arc> &T2,
121  const Fst<Arc> &T3) {
122  {
123  VLOG(1) << "Check destructive and delayed union are equivalent.";
124  VectorFst<Arc> U1(T1);
125  Union(&U1, T2);
126  UnionFst<Arc> U2(T1, T2);
127  CHECK(Equiv(U1, U2));
128  }
129 
130  {
131  VLOG(1) << "Check destructive and delayed concatenation are equivalent.";
132  VectorFst<Arc> C1(T1);
133  Concat(&C1, T2);
134  ConcatFst<Arc> C2(T1, T2);
135  CHECK(Equiv(C1, C2));
136  VectorFst<Arc> C3(T2);
137  Concat(T1, &C3);
138  CHECK(Equiv(C3, C2));
139  }
140 
141  {
142  VLOG(1) << "Check destructive and delayed closure* are equivalent.";
143  VectorFst<Arc> C1(T1);
144  Closure(&C1, CLOSURE_STAR);
146  CHECK(Equiv(C1, C2));
147  }
148 
149  {
150  VLOG(1) << "Check destructive and delayed closure+ are equivalent.";
151  VectorFst<Arc> C1(T1);
152  Closure(&C1, CLOSURE_PLUS);
154  CHECK(Equiv(C1, C2));
155  }
156 
157  {
158  VLOG(1) << "Check union is associative (destructive).";
159  VectorFst<Arc> U1(T1);
160  Union(&U1, T2);
161  Union(&U1, T3);
162 
163  VectorFst<Arc> U3(T2);
164  Union(&U3, T3);
165  VectorFst<Arc> U4(T1);
166  Union(&U4, U3);
167 
168  CHECK(Equiv(U1, U4));
169  }
170 
171  {
172  VLOG(1) << "Check union is associative (delayed).";
173  UnionFst<Arc> U1(T1, T2);
174  UnionFst<Arc> U2(U1, T3);
175 
176  UnionFst<Arc> U3(T2, T3);
177  UnionFst<Arc> U4(T1, U3);
178 
179  CHECK(Equiv(U2, U4));
180  }
181 
182  {
183  VLOG(1) << "Check union is associative (destructive delayed).";
184  UnionFst<Arc> U1(T1, T2);
185  Union(&U1, T3);
186 
187  UnionFst<Arc> U3(T2, T3);
188  UnionFst<Arc> U4(T1, U3);
189 
190  CHECK(Equiv(U1, U4));
191  }
192 
193  {
194  VLOG(1) << "Check concatenation is associative (destructive).";
195  VectorFst<Arc> C1(T1);
196  Concat(&C1, T2);
197  Concat(&C1, T3);
198 
199  VectorFst<Arc> C3(T2);
200  Concat(&C3, T3);
201  VectorFst<Arc> C4(T1);
202  Concat(&C4, C3);
203 
204  CHECK(Equiv(C1, C4));
205  }
206 
207  {
208  VLOG(1) << "Check concatenation is associative (delayed).";
209  ConcatFst<Arc> C1(T1, T2);
210  ConcatFst<Arc> C2(C1, T3);
211 
212  ConcatFst<Arc> C3(T2, T3);
213  ConcatFst<Arc> C4(T1, C3);
214 
215  CHECK(Equiv(C2, C4));
216  }
217 
218  {
219  VLOG(1) << "Check concatenation is associative (destructive delayed).";
220  ConcatFst<Arc> C1(T1, T2);
221  Concat(&C1, T3);
222 
223  ConcatFst<Arc> C3(T2, T3);
224  ConcatFst<Arc> C4(T1, C3);
225 
226  CHECK(Equiv(C1, C4));
227  }
228 
229  if (Weight::Properties() & kLeftSemiring) {
230  VLOG(1) << "Check concatenation left distributes"
231  << " over union (destructive).";
232 
233  VectorFst<Arc> U1(T1);
234  Union(&U1, T2);
235  VectorFst<Arc> C1(T3);
236  Concat(&C1, U1);
237 
238  VectorFst<Arc> C2(T3);
239  Concat(&C2, T1);
240  VectorFst<Arc> C3(T3);
241  Concat(&C3, T2);
242  VectorFst<Arc> U2(C2);
243  Union(&U2, C3);
244 
245  CHECK(Equiv(C1, U2));
246  }
247 
248  if (Weight::Properties() & kRightSemiring) {
249  VLOG(1) << "Check concatenation right distributes"
250  << " over union (destructive).";
251  VectorFst<Arc> U1(T1);
252  Union(&U1, T2);
253  VectorFst<Arc> C1(U1);
254  Concat(&C1, T3);
255 
256  VectorFst<Arc> C2(T1);
257  Concat(&C2, T3);
258  VectorFst<Arc> C3(T2);
259  Concat(&C3, T3);
260  VectorFst<Arc> U2(C2);
261  Union(&U2, C3);
262 
263  CHECK(Equiv(C1, U2));
264  }
265 
266  if (Weight::Properties() & kLeftSemiring) {
267  VLOG(1) << "Check concatenation left distributes over union (delayed).";
268  UnionFst<Arc> U1(T1, T2);
269  ConcatFst<Arc> C1(T3, U1);
270 
271  ConcatFst<Arc> C2(T3, T1);
272  ConcatFst<Arc> C3(T3, T2);
273  UnionFst<Arc> U2(C2, C3);
274 
275  CHECK(Equiv(C1, U2));
276  }
277 
278  if (Weight::Properties() & kRightSemiring) {
279  VLOG(1) << "Check concatenation right distributes over union (delayed).";
280  UnionFst<Arc> U1(T1, T2);
281  ConcatFst<Arc> C1(U1, T3);
282 
283  ConcatFst<Arc> C2(T1, T3);
284  ConcatFst<Arc> C3(T2, T3);
285  UnionFst<Arc> U2(C2, C3);
286 
287  CHECK(Equiv(C1, U2));
288  }
289 
290  if (Weight::Properties() & kLeftSemiring) {
291  VLOG(1) << "Check T T* == T+ (destructive).";
292  VectorFst<Arc> S(T1);
293  Closure(&S, CLOSURE_STAR);
294  VectorFst<Arc> C(T1);
295  Concat(&C, S);
296 
297  VectorFst<Arc> P(T1);
298  Closure(&P, CLOSURE_PLUS);
299 
300  CHECK(Equiv(C, P));
301  }
302 
303  if (Weight::Properties() & kRightSemiring) {
304  VLOG(1) << "Check T* T == T+ (destructive).";
305  VectorFst<Arc> S(T1);
306  Closure(&S, CLOSURE_STAR);
307  VectorFst<Arc> C(S);
308  Concat(&C, T1);
309 
310  VectorFst<Arc> P(T1);
311  Closure(&P, CLOSURE_PLUS);
312 
313  CHECK(Equiv(C, P));
314  }
315 
316  if (Weight::Properties() & kLeftSemiring) {
317  VLOG(1) << "Check T T* == T+ (delayed).";
319  ConcatFst<Arc> C(T1, S);
320 
322 
323  CHECK(Equiv(C, P));
324  }
325 
326  if (Weight::Properties() & kRightSemiring) {
327  VLOG(1) << "Check T* T == T+ (delayed).";
329  ConcatFst<Arc> C(S, T1);
330 
332 
333  CHECK(Equiv(C, P));
334  }
335  }
336 
337  // Tests map-based operations.
338  void TestMap(const Fst<Arc> &T) {
339  {
340  VLOG(1) << "Check destructive and delayed projection are equivalent.";
341  VectorFst<Arc> P1(T);
344  CHECK(Equiv(P1, P2));
345  }
346 
347  {
348  VLOG(1) << "Check destructive and delayed inversion are equivalent.";
349  VectorFst<Arc> I1(T);
350  Invert(&I1);
351  InvertFst<Arc> I2(T);
352  CHECK(Equiv(I1, I2));
353  }
354 
355  {
356  VLOG(1) << "Check Pi_1(T) = Pi_2(T^-1) (destructive).";
357  VectorFst<Arc> P1(T);
358  VectorFst<Arc> I1(T);
360  Invert(&I1);
362  CHECK(Equiv(P1, I1));
363  }
364 
365  {
366  VLOG(1) << "Check Pi_2(T) = Pi_1(T^-1) (destructive).";
367  VectorFst<Arc> P1(T);
368  VectorFst<Arc> I1(T);
370  Invert(&I1);
372  CHECK(Equiv(P1, I1));
373  }
374 
375  {
376  VLOG(1) << "Check Pi_1(T) = Pi_2(T^-1) (delayed).";
378  InvertFst<Arc> I1(T);
380  CHECK(Equiv(P1, P2));
381  }
382 
383  {
384  VLOG(1) << "Check Pi_2(T) = Pi_1(T^-1) (delayed).";
386  InvertFst<Arc> I1(T);
388  CHECK(Equiv(P1, P2));
389  }
390 
391  {
392  VLOG(1) << "Check destructive relabeling";
393  static const int kNumLabels = 10;
394  // set up relabeling pairs
395  std::vector<Label> labelset(kNumLabels);
396  for (size_t i = 0; i < kNumLabels; ++i) labelset[i] = i;
397  for (size_t i = 0; i < kNumLabels; ++i) {
398  using std::swap;
399  const auto index =
400  std::uniform_int_distribution<>(0, kNumLabels - 1)(rand_);
401  swap(labelset[i], labelset[index]);
402  }
403 
404  std::vector<std::pair<Label, Label>> ipairs1(kNumLabels);
405  std::vector<std::pair<Label, Label>> opairs1(kNumLabels);
406  for (size_t i = 0; i < kNumLabels; ++i) {
407  ipairs1[i] = std::make_pair(i, labelset[i]);
408  opairs1[i] = std::make_pair(labelset[i], i);
409  }
410  VectorFst<Arc> R(T);
411  Relabel(&R, ipairs1, opairs1);
412 
413  std::vector<std::pair<Label, Label>> ipairs2(kNumLabels);
414  std::vector<std::pair<Label, Label>> opairs2(kNumLabels);
415  for (size_t i = 0; i < kNumLabels; ++i) {
416  ipairs2[i] = std::make_pair(labelset[i], i);
417  opairs2[i] = std::make_pair(i, labelset[i]);
418  }
419  Relabel(&R, ipairs2, opairs2);
420  CHECK(Equiv(R, T));
421 
422  VLOG(1) << "Check on-the-fly relabeling";
423  RelabelFst<Arc> Rdelay(T, ipairs1, opairs1);
424 
425  RelabelFst<Arc> RRdelay(Rdelay, ipairs2, opairs2);
426  CHECK(Equiv(RRdelay, T));
427  }
428 
429  {
430  VLOG(1) << "Check encoding/decoding (destructive).";
431  VectorFst<Arc> D(T);
432  uint8_t encode_props = 0;
433  if (std::bernoulli_distribution(.5)(rand_)) {
434  encode_props |= kEncodeLabels;
435  }
436  if (std::bernoulli_distribution(.5)(rand_)) {
437  encode_props |= kEncodeWeights;
438  }
439  EncodeMapper<Arc> encoder(encode_props, ENCODE);
440  Encode(&D, &encoder);
441  Decode(&D, encoder);
442  CHECK(Equiv(D, T));
443  }
444 
445  {
446  VLOG(1) << "Check encoding/decoding (delayed).";
447  uint8_t encode_props = 0;
448  if (std::bernoulli_distribution(.5)(rand_)) {
449  encode_props |= kEncodeLabels;
450  }
451  if (std::bernoulli_distribution(.5)(rand_)) {
452  encode_props |= kEncodeWeights;
453  }
454  EncodeMapper<Arc> encoder(encode_props, ENCODE);
455  EncodeFst<Arc> E(T, &encoder);
456  VectorFst<Arc> Encoded(E);
457  DecodeFst<Arc> D(Encoded, encoder);
458  CHECK(Equiv(D, T));
459  }
460 
461  {
462  VLOG(1) << "Check gallic mappers (constructive).";
463  ToGallicMapper<Arc> to_mapper;
464  FromGallicMapper<Arc> from_mapper;
466  VectorFst<Arc> F;
467  ArcMap(T, &G, to_mapper);
468  ArcMap(G, &F, from_mapper);
469  CHECK(Equiv(T, F));
470  }
471 
472  {
473  VLOG(1) << "Check gallic mappers (delayed).";
476  CHECK(Equiv(T, F));
477  }
478  }
479 
480  // Tests compose-based operations.
481  void TestCompose(const Fst<Arc> &T1, const Fst<Arc> &T2, const Fst<Arc> &T3) {
482  if (!(Weight::Properties() & kCommutative)) return;
483 
484  VectorFst<Arc> S1(T1);
485  VectorFst<Arc> S2(T2);
486  VectorFst<Arc> S3(T3);
487 
488  ILabelCompare<Arc> icomp;
489  OLabelCompare<Arc> ocomp;
490 
491  ArcSort(&S1, ocomp);
492  ArcSort(&S2, ocomp);
493  ArcSort(&S3, icomp);
494 
495  {
496  VLOG(1) << "Check composition is associative.";
497  ComposeFst<Arc> C1(S1, S2);
498  ComposeFst<Arc> C2(C1, S3);
499  ComposeFst<Arc> C3(S2, S3);
500  ComposeFst<Arc> C4(S1, C3);
501 
502  CHECK(Equiv(C2, C4));
503  }
504 
505  {
506  VLOG(1) << "Check composition left distributes over union.";
507  UnionFst<Arc> U1(S2, S3);
508  ComposeFst<Arc> C1(S1, U1);
509 
510  ComposeFst<Arc> C2(S1, S2);
511  ComposeFst<Arc> C3(S1, S3);
512  UnionFst<Arc> U2(C2, C3);
513 
514  CHECK(Equiv(C1, U2));
515  }
516 
517  {
518  VLOG(1) << "Check composition right distributes over union.";
519  UnionFst<Arc> U1(S1, S2);
520  ComposeFst<Arc> C1(U1, S3);
521 
522  ComposeFst<Arc> C2(S1, S3);
523  ComposeFst<Arc> C3(S2, S3);
524  UnionFst<Arc> U2(C2, C3);
525 
526  CHECK(Equiv(C1, U2));
527  }
528 
529  VectorFst<Arc> A1(S1);
530  VectorFst<Arc> A2(S2);
531  VectorFst<Arc> A3(S3);
535 
536  {
537  VLOG(1) << "Check intersection is commutative.";
538  IntersectFst<Arc> I1(A1, A2);
539  IntersectFst<Arc> I2(A2, A1);
540  CHECK(Equiv(I1, I2));
541  }
542 
543  {
544  VLOG(1) << "Check all epsilon filters leads to equivalent results.";
545  using M = Matcher<Fst<Arc>>;
546  ComposeFst<Arc> C1(S1, S2);
547  ComposeFst<Arc> C2(
549  ComposeFst<Arc> C3(S1, S2,
551 
552  CHECK(Equiv(C1, C2));
553  CHECK(Equiv(C1, C3));
554 
555  if ((Weight::Properties() & kIdempotent) ||
556  S1.Properties(kNoOEpsilons, false) ||
557  S2.Properties(kNoIEpsilons, false)) {
558  ComposeFst<Arc> C4(
559  S1, S2, ComposeFstOptions<Arc, M, TrivialComposeFilter<M>>());
560  CHECK(Equiv(C1, C4));
561  ComposeFst<Arc> C5(
562  S1, S2, ComposeFstOptions<Arc, M, NoMatchComposeFilter<M>>());
563  CHECK(Equiv(C1, C5));
564  }
565 
566  if (S1.Properties(kNoOEpsilons, false) &&
567  S2.Properties(kNoIEpsilons, false)) {
568  ComposeFst<Arc> C6(S1, S2,
570  CHECK(Equiv(C1, C6));
571  }
572  }
573 
574  {
575  VLOG(1) << "Check look-ahead filters lead to equivalent results.";
576  VectorFst<Arc> C1, C2;
577  Compose(S1, S2, &C1);
578  LookAheadCompose(S1, S2, &C2);
579  CHECK(Equiv(C1, C2));
580  }
581  }
582 
583  // Tests sorting operations
584  void TestSort(const Fst<Arc> &T) {
585  ILabelCompare<Arc> icomp;
586  OLabelCompare<Arc> ocomp;
587 
588  {
589  VLOG(1) << "Check arc sorted Fst is equivalent to its input.";
590  VectorFst<Arc> S1(T);
591  ArcSort(&S1, icomp);
592  CHECK(Equiv(T, S1));
593  }
594 
595  {
596  VLOG(1) << "Check destructive and delayed arcsort are equivalent.";
597  VectorFst<Arc> S1(T);
598  ArcSort(&S1, icomp);
600  CHECK(Equiv(S1, S2));
601  }
602 
603  {
604  VLOG(1) << "Check ilabel sorting vs. olabel sorting with inversions.";
605  VectorFst<Arc> S1(T);
606  VectorFst<Arc> S2(T);
607  ArcSort(&S1, icomp);
608  Invert(&S2);
609  ArcSort(&S2, ocomp);
610  Invert(&S2);
611  CHECK(Equiv(S1, S2));
612  }
613 
614  {
615  VLOG(1) << "Check topologically sorted Fst is equivalent to its input.";
616  VectorFst<Arc> S1(T);
617  TopSort(&S1);
618  CHECK(Equiv(T, S1));
619  }
620 
621  {
622  VLOG(1) << "Check reverse(reverse(T)) = T";
623  for (int i = 0; i < 2; ++i) {
625  VectorFst<Arc> R2;
626  bool require_superinitial = i == 1;
627  Reverse(T, &R1, require_superinitial);
628  Reverse(R1, &R2, require_superinitial);
629  CHECK(Equiv(T, R2));
630  }
631  }
632  }
633 
634  // Tests optimization operations
635  void TestOptimize(const Fst<Arc> &T) {
636  uint64_t tprops = T.Properties(kFstProperties, true);
637  uint64_t wprops = Weight::Properties();
638 
639  VectorFst<Arc> A(T);
641  {
642  VLOG(1) << "Check connected FST is equivalent to its input.";
643  VectorFst<Arc> C1(T);
644  Connect(&C1);
645  CHECK(Equiv(T, C1));
646  }
647 
648  if ((wprops & kSemiring) == kSemiring &&
649  (tprops & kAcyclic || wprops & kIdempotent)) {
650  VLOG(1) << "Check epsilon-removed FST is equivalent to its input.";
651  VectorFst<Arc> R1(T);
652  RmEpsilon(&R1);
653  CHECK(Equiv(T, R1));
654 
655  VLOG(1) << "Check destructive and delayed epsilon removal"
656  << "are equivalent.";
657  RmEpsilonFst<Arc> R2(T);
658  CHECK(Equiv(R1, R2));
659 
660  VLOG(1) << "Check an FST with a large proportion"
661  << " of epsilon transitions:";
662  // Maps all transitions of T to epsilon-transitions and append
663  // a non-epsilon transition.
664  VectorFst<Arc> U;
665  ArcMap(T, &U, EpsMapper<Arc>());
666  VectorFst<Arc> V;
667  V.SetStart(V.AddState());
668  Arc arc(1, 1, Weight::One(), V.AddState());
669  V.AddArc(V.Start(), arc);
670  V.SetFinal(arc.nextstate, Weight::One());
671  Concat(&U, V);
672  // Check that epsilon-removal preserves the shortest-distance
673  // from the initial state to the final states.
674  std::vector<Weight> d;
675  ShortestDistance(U, &d, true);
676  Weight w = U.Start() < d.size() ? d[U.Start()] : Weight::Zero();
677  VectorFst<Arc> U1(U);
678  RmEpsilon(&U1);
679  ShortestDistance(U1, &d, true);
680  Weight w1 = U1.Start() < d.size() ? d[U1.Start()] : Weight::Zero();
681  CHECK(ApproxEqual(w, w1, kTestDelta));
682  RmEpsilonFst<Arc> U2(U);
683  ShortestDistance(U2, &d, true);
684  Weight w2 = U2.Start() < d.size() ? d[U2.Start()] : Weight::Zero();
685  CHECK(ApproxEqual(w, w2, kTestDelta));
686  }
687 
688  if ((wprops & kSemiring) == kSemiring && tprops & kAcyclic) {
689  VLOG(1) << "Check determinized FSA is equivalent to its input.";
690  DeterminizeFst<Arc> D(A);
691  CHECK(Equiv(A, D));
692 
693  {
694  VLOG(1) << "Check determinized FST is equivalent to its input.";
697  DeterminizeFst<Arc> DT(T, opts);
698  CHECK(Equiv(T, DT));
699  }
700 
701  if ((wprops & (kPath | kCommutative)) == (kPath | kCommutative)) {
702  VLOG(1) << "Check pruning in determinization";
703  VectorFst<Arc> P;
704  const Weight threshold = generate_();
706  opts.weight_threshold = threshold;
707  Determinize(A, &P, opts);
708  CHECK(P.Properties(kIDeterministic, true));
709  CHECK(PruneEquiv(A, P, threshold));
710  }
711 
712  if ((wprops & kPath) == kPath) {
713  VLOG(1) << "Check min-determinization";
714 
715  // Ensures no input epsilons
716  VectorFst<Arc> R(T);
717  std::vector<std::pair<Label, Label>> ipairs, opairs;
718  ipairs.push_back(std::pair<Label, Label>(0, 1));
719  Relabel(&R, ipairs, opairs);
720 
721  VectorFst<Arc> M;
724  Determinize(R, &M, opts);
725  CHECK(M.Properties(kIDeterministic, true));
726  CHECK(MinRelated(M, R));
727  }
728 
729  int n;
730  {
731  VLOG(1) << "Check size(min(det(A))) <= size(det(A))"
732  << " and min(det(A)) equiv det(A)";
733  VectorFst<Arc> M(D);
734  n = M.NumStates();
735  Minimize(&M, static_cast<MutableFst<Arc> *>(nullptr), kDelta);
736  CHECK(Equiv(D, M));
737  CHECK(M.NumStates() <= n);
738  n = M.NumStates();
739  }
740 
741  if (n && (wprops & kIdempotent) == kIdempotent &&
742  A.Properties(kNoEpsilons, true)) {
743  VLOG(1) << "Check that Revuz's algorithm leads to the"
744  << " same number of states as Brozozowski's algorithm";
745 
746  // Skip test if A is the empty machine or contains epsilons or
747  // if the semiring is not idempotent (to avoid floating point
748  // errors)
750  Reverse(A, &R);
751  RmEpsilon(&R);
753  VectorFst<Arc> RD;
754  Reverse(DR, &RD);
755  DeterminizeFst<Arc> DRD(RD);
756  VectorFst<Arc> M(DRD);
757  CHECK_EQ(n + 1, M.NumStates()); // Accounts for the epsilon transition
758  // to the initial state
759  }
760  }
761 
762  if ((wprops & kSemiring) == kSemiring && tprops & kAcyclic) {
763  VLOG(1) << "Check disambiguated FSA is equivalent to its input.";
764  VectorFst<Arc> R(A), D;
765  RmEpsilon(&R);
766  Disambiguate(R, &D);
767  CHECK(Equiv(R, D));
768  VLOG(1) << "Check disambiguated FSA is unambiguous";
769  CHECK(Unambiguous(D));
770 
771  /* TODO(riley): find out why this fails
772  if ((wprops & (kPath | kCommutative)) == (kPath | kCommutative)) {
773  VLOG(1) << "Check pruning in disambiguation";
774  VectorFst<Arc> P;
775  const Weight threshold = generate_();
776  DisambiguateOptions<Arc> opts;
777  opts.weight_threshold = threshold;
778  Disambiguate(R, &P, opts);
779  CHECK(Unambiguous(P));
780  CHECK(PruneEquiv(A, P, threshold));
781  }
782  */
783  }
784 
785  if (Arc::Type() == LogArc::Type() || Arc::Type() == StdArc::Type()) {
786  VLOG(1) << "Check reweight(T) equiv T";
787  std::vector<Weight> potential;
788  VectorFst<Arc> RI(T);
789  VectorFst<Arc> RF(T);
790  while (potential.size() < RI.NumStates()) {
791  potential.push_back(generate_());
792  }
793 
794  Reweight(&RI, potential, REWEIGHT_TO_INITIAL);
795  CHECK(Equiv(T, RI));
796 
797  Reweight(&RF, potential, REWEIGHT_TO_FINAL);
798  CHECK(Equiv(T, RF));
799  }
800 
801  if ((wprops & kIdempotent) || (tprops & kAcyclic)) {
802  VLOG(1) << "Check pushed FST is equivalent to input FST.";
803  // Pushing towards the final state.
804  if (wprops & kRightSemiring) {
805  VectorFst<Arc> P1;
806  Push<Arc, REWEIGHT_TO_FINAL>(T, &P1, kPushLabels);
807  CHECK(Equiv(T, P1));
808 
809  VectorFst<Arc> P2;
810  Push<Arc, REWEIGHT_TO_FINAL>(T, &P2, kPushWeights);
811  CHECK(Equiv(T, P2));
812 
813  VectorFst<Arc> P3;
814  Push<Arc, REWEIGHT_TO_FINAL>(T, &P3, kPushLabels | kPushWeights);
815  CHECK(Equiv(T, P3));
816  }
817 
818  // Pushing towards the initial state.
819  if (wprops & kLeftSemiring) {
820  VectorFst<Arc> P1;
821  Push<Arc, REWEIGHT_TO_INITIAL>(T, &P1, kPushLabels);
822  CHECK(Equiv(T, P1));
823 
824  VectorFst<Arc> P2;
825  Push<Arc, REWEIGHT_TO_INITIAL>(T, &P2, kPushWeights);
826  CHECK(Equiv(T, P2));
827  VectorFst<Arc> P3;
828  Push<Arc, REWEIGHT_TO_INITIAL>(T, &P3, kPushLabels | kPushWeights);
829  CHECK(Equiv(T, P3));
830  }
831  }
832 
833  if constexpr (IsPath<Weight>::value) {
834  if ((wprops & (kPath | kCommutative)) == (kPath | kCommutative)) {
835  VLOG(1) << "Check pruning algorithm";
836  {
837  VLOG(1) << "Check equiv. of constructive and destructive algorithms";
838  const Weight threshold = generate_();
839  VectorFst<Arc> P1(T);
840  Prune(&P1, threshold);
841  VectorFst<Arc> P2;
842  Prune(T, &P2, threshold);
843  CHECK(Equiv(P1, P2));
844  }
845 
846  {
847  VLOG(1) << "Check prune(reverse) equiv reverse(prune)";
848  const Weight threshold = generate_();
850  VectorFst<Arc> P1(T);
851  VectorFst<Arc> P2;
852  Prune(&P1, threshold);
853  Reverse(T, &R);
854  Prune(&R, threshold.Reverse());
855  Reverse(R, &P2);
856  CHECK(Equiv(P1, P2));
857  }
858  {
859  VLOG(1) << "Check: ShortestDistance(A - prune(A))"
860  << " > ShortestDistance(A) times Threshold";
861  const Weight threshold = generate_();
862  VectorFst<Arc> P;
863  Prune(A, &P, threshold);
864  CHECK(PruneEquiv(A, P, threshold));
865  }
866  }
867  }
868  if (tprops & kAcyclic) {
869  VLOG(1) << "Check synchronize(T) equiv T";
870  SynchronizeFst<Arc> S(T);
871  CHECK(Equiv(T, S));
872  }
873  }
874 
875  // Tests search operations
876  void TestSearch(const Fst<Arc> &T) {
877  if constexpr (IsPath<Weight>::value) {
878  uint64_t wprops = Weight::Properties();
879 
880  VectorFst<Arc> A(T);
882 
883  if ((wprops & (kPath | kRightSemiring)) == (kPath | kRightSemiring)) {
884  VLOG(1) << "Check 1-best weight.";
885  VectorFst<Arc> path;
886  ShortestPath(T, &path);
887  Weight tsum = ShortestDistance(T);
888  Weight psum = ShortestDistance(path);
889  CHECK(ApproxEqual(tsum, psum, kTestDelta));
890  }
891 
892  if ((wprops & (kPath | kSemiring)) == (kPath | kSemiring)) {
893  VLOG(1) << "Check n-best weights";
894  VectorFst<Arc> R(A);
895  RmEpsilon(&R, /*connect=*/true, Arc::Weight::Zero(), kNoStateId,
896  kDelta);
897  const int nshortest = std::uniform_int_distribution<>(
898  0, kNumRandomShortestPaths + 1)(rand_);
899  VectorFst<Arc> paths;
900  ShortestPath(R, &paths, nshortest, /*unique=*/true,
901  /*first_path=*/false, Weight::Zero(), kNumShortestStates,
902  kDelta);
903  std::vector<Weight> distance;
904  ShortestDistance(paths, &distance, true, kDelta);
905  StateId pstart = paths.Start();
906  if (pstart != kNoStateId) {
907  ArcIterator<Fst<Arc>> piter(paths, pstart);
908  for (; !piter.Done(); piter.Next()) {
909  StateId s = piter.Value().nextstate;
910  Weight nsum = s < distance.size()
911  ? Times(piter.Value().weight, distance[s])
912  : Weight::Zero();
913  VectorFst<Arc> path;
914  ShortestPath(R, &path, 1, false, false, Weight::Zero(), kNoStateId,
915  kDelta);
916  Weight dsum = ShortestDistance(path, kDelta);
917  CHECK(ApproxEqual(nsum, dsum, kTestDelta));
918  ArcMap(&path, RmWeightMapper<Arc>());
919  VectorFst<Arc> S;
920  Difference(R, path, &S);
921  R = S;
922  }
923  }
924  }
925  }
926  }
927 
928  // Tests if two FSTS are equivalent by checking if random
929  // strings from one FST are transduced the same by both FSTs.
930  template <class A>
931  bool Equiv(const Fst<A> &fst1, const Fst<A> &fst2) {
932  VLOG(1) << "Check FSTs for sanity (including property bits).";
933  CHECK(Verify(fst1));
934  CHECK(Verify(fst2));
935 
936  // Ensures seed used once per instantiation.
937  static const UniformArcSelector<A> uniform_selector(seed_);
938  const RandGenOptions<UniformArcSelector<A>> opts(uniform_selector,
939  kRandomPathLength);
940  return RandEquivalent(fst1, fst2, kNumRandomPaths, opts, kTestDelta, seed_);
941  }
942 
943  // Tests FSA is unambiguous.
944  bool Unambiguous(const Fst<Arc> &fst) {
945  VectorFst<StdArc> sfst, dfst;
946  VectorFst<LogArc> lfst1, lfst2;
947  ArcMap(fst, &sfst, RmWeightMapper<Arc, StdArc>());
948  Determinize(sfst, &dfst);
949  ArcMap(fst, &lfst1, RmWeightMapper<Arc, LogArc>());
950  ArcMap(dfst, &lfst2, RmWeightMapper<StdArc, LogArc>());
951  return Equiv(lfst1, lfst2);
952  }
953 
954  // Ensures input-epsilon free transducers fst1 and fst2 have the
955  // same domain and that for each string pair '(is, os)' in fst1,
956  // '(is, os)' is the minimum weight match to 'is' in fst2.
957  template <class A>
958  bool MinRelated(const Fst<A> &fst1, const Fst<A> &fst2) {
959  // Same domain
960  VectorFst<Arc> P1(fst1), P2(fst2);
963  if (!Equiv(P1, P2)) {
964  LOG(ERROR) << "Inputs not equivalent";
965  return false;
966  }
967 
968  // Ensures seed used once per instantiation.
969  static const UniformArcSelector<A> uniform_selector(seed_);
970  const RandGenOptions<UniformArcSelector<A>> opts(uniform_selector,
971  kRandomPathLength);
972 
973  VectorFst<Arc> path, paths1, paths2;
974  for (ssize_t n = 0; n < kNumRandomPaths; ++n) {
975  RandGen(fst1, &path, opts);
976  Invert(&path);
977  ArcMap(&path, RmWeightMapper<Arc>());
978  Compose(path, fst2, &paths1);
979  Weight sum1 = ShortestDistance(paths1);
980  Compose(paths1, path, &paths2);
981  Weight sum2 = ShortestDistance(paths2);
982  if (!ApproxEqual(Plus(sum1, sum2), sum2, kTestDelta)) {
983  LOG(ERROR) << "Sums not equivalent: " << sum1 << " " << sum2;
984  return false;
985  }
986  }
987  return true;
988  }
989 
990  // Tests ShortestDistance(A - P) >= ShortestDistance(A) times Threshold.
991  template <class A>
992  bool PruneEquiv(const Fst<A> &fst, const Fst<A> &pfst, Weight threshold) {
993  VLOG(1) << "Check FSTs for sanity (including property bits).";
994  CHECK(Verify(fst));
995  CHECK(Verify(pfst));
996 
998  ArcMapFst(pfst, RmWeightMapper<Arc>()))));
999  const Weight sum1 = Times(ShortestDistance(fst), threshold);
1000  const Weight sum2 = ShortestDistance(D);
1001  return ApproxEqual(Plus(sum1, sum2), sum1, kTestDelta);
1002  }
1003 
1004  // Random seed.
1005  uint64_t seed_;
1006  // Random state (for randomness in this class).
1007  std::mt19937_64 rand_;
1008  // FST with no states
1009  VectorFst<Arc> zero_fst_;
1010  // FST with one state that accepts epsilon.
1011  VectorFst<Arc> one_fst_;
1012  // FST with one state that accepts all strings.
1013  VectorFst<Arc> univ_fst_;
1014  // Generates weights used in testing.
1015  WeightGenerator generate_;
1016  // Maximum random path length.
1017  static constexpr int kRandomPathLength = 25;
1018  // Number of random paths to explore.
1019  static constexpr int kNumRandomPaths = 100;
1020  // Maximum number of nshortest paths.
1021  static constexpr int kNumRandomShortestPaths = 100;
1022  // Maximum number of nshortest states.
1023  static constexpr int kNumShortestStates = 10000;
1024  // Delta for equivalence tests.
1025  static constexpr float kTestDelta = .05;
1026 
1027  WeightedTester(const WeightedTester &) = delete;
1028  WeightedTester &operator=(const WeightedTester &) = delete;
1029 };
1030 
1031 // This class tests a variety of identities and properties that must
1032 // hold for various algorithms on unweighted FSAs and that are not tested
1033 // by WeightedTester. Only the specialization does anything interesting.
1034 template <class Arc>
1036  public:
1037  UnweightedTester(const Fst<Arc> &zero_fsa, const Fst<Arc> &one_fsa,
1038  const Fst<Arc> &univ_fsa, uint64_t seed) {}
1039 
1040  void Test(const Fst<Arc> &A1, const Fst<Arc> &A2, const Fst<Arc> &A3) {}
1041 };
1042 
1043 // Specialization for StdArc. This should work for any commutative,
1044 // idempotent semiring when restricted to the unweighted case
1045 // (being isomorphic to the boolean semiring).
1046 template <>
1048  public:
1049  using Arc = StdArc;
1053 
1054  UnweightedTester(const Fst<Arc> &zero_fsa, const Fst<Arc> &one_fsa,
1055  const Fst<Arc> &univ_fsa, uint64_t seed)
1056  : zero_fsa_(zero_fsa),
1057  one_fsa_(one_fsa),
1058  univ_fsa_(univ_fsa),
1059  rand_(seed) {}
1060 
1061  void Test(const Fst<Arc> &A1, const Fst<Arc> &A2, const Fst<Arc> &A3) {
1062  TestRational(A1, A2, A3);
1063  TestIntersect(A1, A2, A3);
1064  TestOptimize(A1);
1065  }
1066 
1067  private:
1068  // Tests rational operations with identities.
1069  void TestRational(const Fst<Arc> &A1, const Fst<Arc> &A2,
1070  const Fst<Arc> &A3) {
1071  {
1072  VLOG(1) << "Check the union contains its arguments (destructive).";
1073  VectorFst<Arc> U(A1);
1074  Union(&U, A2);
1075 
1076  CHECK(Subset(A1, U));
1077  CHECK(Subset(A2, U));
1078  }
1079 
1080  {
1081  VLOG(1) << "Check the union contains its arguments (delayed).";
1082  UnionFst<Arc> U(A1, A2);
1083 
1084  CHECK(Subset(A1, U));
1085  CHECK(Subset(A2, U));
1086  }
1087 
1088  {
1089  VLOG(1) << "Check if A^n c A* (destructive).";
1090  VectorFst<Arc> C(one_fsa_);
1091  const int n = std::uniform_int_distribution<>(0, 4)(rand_);
1092  for (int i = 0; i < n; ++i) Concat(&C, A1);
1093 
1094  VectorFst<Arc> S(A1);
1095  Closure(&S, CLOSURE_STAR);
1096  CHECK(Subset(C, S));
1097  }
1098 
1099  {
1100  VLOG(1) << "Check if A^n c A* (delayed).";
1101  const int n = std::uniform_int_distribution<>(0, 4)(rand_);
1102  std::unique_ptr<Fst<Arc>> C = std::make_unique<VectorFst<Arc>>(one_fsa_);
1103  for (int i = 0; i < n; ++i) {
1104  C = std::make_unique<ConcatFst<Arc>>(*C, A1);
1105  }
1107  CHECK(Subset(*C, S));
1108  }
1109  }
1110 
1111  // Tests intersect-based operations.
1112  void TestIntersect(const Fst<Arc> &A1, const Fst<Arc> &A2,
1113  const Fst<Arc> &A3) {
1114  VectorFst<Arc> S1(A1);
1115  VectorFst<Arc> S2(A2);
1116  VectorFst<Arc> S3(A3);
1117 
1118  ILabelCompare<Arc> comp;
1119 
1120  ArcSort(&S1, comp);
1121  ArcSort(&S2, comp);
1122  ArcSort(&S3, comp);
1123 
1124  {
1125  VLOG(1) << "Check the intersection is contained in its arguments.";
1126  IntersectFst<Arc> I1(S1, S2);
1127  CHECK(Subset(I1, S1));
1128  CHECK(Subset(I1, S2));
1129  }
1130 
1131  {
1132  VLOG(1) << "Check union distributes over intersection.";
1133  IntersectFst<Arc> I1(S1, S2);
1134  UnionFst<Arc> U1(I1, S3);
1135 
1136  UnionFst<Arc> U2(S1, S3);
1137  UnionFst<Arc> U3(S2, S3);
1138  ArcSortFst<Arc, ILabelCompare<Arc>> S4(U3, comp);
1139  IntersectFst<Arc> I2(U2, S4);
1140 
1141  CHECK(Equiv(U1, I2));
1142  }
1143 
1144  VectorFst<Arc> C1;
1145  VectorFst<Arc> C2;
1146  Complement(S1, &C1);
1147  Complement(S2, &C2);
1148  ArcSort(&C1, comp);
1149  ArcSort(&C2, comp);
1150 
1151  {
1152  VLOG(1) << "Check S U S' = Sigma*";
1153  UnionFst<Arc> U(S1, C1);
1154  CHECK(Equiv(U, univ_fsa_));
1155  }
1156 
1157  {
1158  VLOG(1) << "Check S n S' = {}";
1159  IntersectFst<Arc> I(S1, C1);
1160  CHECK(Equiv(I, zero_fsa_));
1161  }
1162 
1163  {
1164  VLOG(1) << "Check (S1' U S2') == (S1 n S2)'";
1165  UnionFst<Arc> U(C1, C2);
1166 
1167  IntersectFst<Arc> I(S1, S2);
1168  VectorFst<Arc> C3;
1169  Complement(I, &C3);
1170  CHECK(Equiv(U, C3));
1171  }
1172 
1173  {
1174  VLOG(1) << "Check (S1' n S2') == (S1 U S2)'";
1175  IntersectFst<Arc> I(C1, C2);
1176 
1177  UnionFst<Arc> U(S1, S2);
1178  VectorFst<Arc> C3;
1179  Complement(U, &C3);
1180  CHECK(Equiv(I, C3));
1181  }
1182  }
1183 
1184  // Tests optimization operations.
1185  void TestOptimize(const Fst<Arc> &A) {
1186  {
1187  VLOG(1) << "Check determinized FSA is equivalent to its input.";
1188  DeterminizeFst<Arc> D(A);
1189  CHECK(Equiv(A, D));
1190  }
1191 
1192  {
1193  VLOG(1) << "Check disambiguated FSA is equivalent to its input.";
1194  VectorFst<Arc> R(A), D;
1195  RmEpsilon(&R);
1196 
1197  Disambiguate(R, &D);
1198  CHECK(Equiv(R, D));
1199  }
1200 
1201  {
1202  VLOG(1) << "Check minimized FSA is equivalent to its input.";
1203  int n;
1204  {
1205  RmEpsilonFst<Arc> R(A);
1206  DeterminizeFst<Arc> D(R);
1207  VectorFst<Arc> M(D);
1208  Minimize(&M, static_cast<MutableFst<Arc> *>(nullptr), kDelta);
1209  CHECK(Equiv(A, M));
1210  n = M.NumStates();
1211  }
1212 
1213  if (n) { // Skips test if A is the empty machine.
1214  VLOG(1) << "Check that Hopcroft's and Revuz's algorithms lead to the"
1215  << " same number of states as Brozozowski's algorithm";
1216  VectorFst<Arc> R;
1217  Reverse(A, &R);
1218  RmEpsilon(&R);
1219  DeterminizeFst<Arc> DR(R);
1220  VectorFst<Arc> RD;
1221  Reverse(DR, &RD);
1222  DeterminizeFst<Arc> DRD(RD);
1223  VectorFst<Arc> M(DRD);
1224  CHECK_EQ(n + 1, M.NumStates()); // Accounts for the epsilon transition
1225  // to the initial state.
1226  }
1227  }
1228  }
1229 
1230  // Tests if two FSAS are equivalent.
1231  bool Equiv(const Fst<Arc> &fsa1, const Fst<Arc> &fsa2) {
1232  VLOG(1) << "Check FSAs for sanity (including property bits).";
1233  CHECK(Verify(fsa1));
1234  CHECK(Verify(fsa2));
1235 
1236  VectorFst<Arc> vfsa1(fsa1);
1237  VectorFst<Arc> vfsa2(fsa2);
1238  RmEpsilon(&vfsa1);
1239  RmEpsilon(&vfsa2);
1240  DeterminizeFst<Arc> dfa1(vfsa1);
1241  DeterminizeFst<Arc> dfa2(vfsa2);
1242 
1243  // Test equivalence using union-find algorithm
1244  bool equiv1 = Equivalent(dfa1, dfa2);
1245 
1246  // Test equivalence by checking if (S1 - S2) U (S2 - S1) is empty
1247  ILabelCompare<Arc> comp;
1248  VectorFst<Arc> sdfa1(dfa1);
1249  ArcSort(&sdfa1, comp);
1250  VectorFst<Arc> sdfa2(dfa2);
1251  ArcSort(&sdfa2, comp);
1252 
1253  DifferenceFst<Arc> dfsa1(sdfa1, sdfa2);
1254  DifferenceFst<Arc> dfsa2(sdfa2, sdfa1);
1255 
1256  VectorFst<Arc> ufsa(dfsa1);
1257  Union(&ufsa, dfsa2);
1258  Connect(&ufsa);
1259  bool equiv2 = ufsa.NumStates() == 0;
1260 
1261  // Checks both equivalence tests match.
1262  CHECK((equiv1 && equiv2) || (!equiv1 && !equiv2));
1263 
1264  return equiv1;
1265  }
1266 
1267  // Tests if FSA1 is a subset of FSA2 (disregarding weights).
1268  bool Subset(const Fst<Arc> &fsa1, const Fst<Arc> &fsa2) {
1269  VLOG(1) << "Check FSAs (incl. property bits) for sanity";
1270  CHECK(Verify(fsa1));
1271  CHECK(Verify(fsa2));
1272 
1273  VectorFst<StdArc> vfsa1;
1274  VectorFst<StdArc> vfsa2;
1275  RmEpsilon(&vfsa1);
1276  RmEpsilon(&vfsa2);
1277  ILabelCompare<StdArc> comp;
1278  ArcSort(&vfsa1, comp);
1279  ArcSort(&vfsa2, comp);
1280  IntersectFst<StdArc> ifsa(vfsa1, vfsa2);
1281  DeterminizeFst<StdArc> dfa1(vfsa1);
1282  DeterminizeFst<StdArc> dfa2(ifsa);
1283  return Equivalent(dfa1, dfa2);
1284  }
1285 
1286  // Returns complement FSA.
1287  void Complement(const Fst<Arc> &ifsa, MutableFst<Arc> *ofsa) {
1288  RmEpsilonFst<Arc> rfsa(ifsa);
1289  DeterminizeFst<Arc> dfa(rfsa);
1290  DifferenceFst<Arc> cfsa(univ_fsa_, dfa);
1291  *ofsa = cfsa;
1292  }
1293 
1294  // FSA with no states.
1295  VectorFst<Arc> zero_fsa_;
1296  // FSA with one state that accepts epsilon.
1297  VectorFst<Arc> one_fsa_;
1298  // FSA with one state that accepts all strings.
1299  VectorFst<Arc> univ_fsa_;
1300  // Random state.
1301  std::mt19937_64 rand_;
1302 };
1303 
1304 // This class tests a variety of identities and properties that must
1305 // hold for various FST algorithms. It randomly generates FSTs, using
1306 // function object 'weight_generator' to select weights. 'WeightTester'
1307 // and 'UnweightedTester' are then called.
1308 template <class Arc>
1309 class AlgoTester {
1310  public:
1311  using Label = typename Arc::Label;
1312  using StateId = typename Arc::StateId;
1313  using Weight = typename Arc::Weight;
1315 
1316  AlgoTester(WeightGenerator generator, uint64_t seed)
1317  : generate_(std::move(generator)), rand_(seed) {
1318  one_fst_.AddState();
1319  one_fst_.SetStart(0);
1320  one_fst_.SetFinal(0);
1321 
1322  univ_fst_.AddState();
1323  univ_fst_.SetStart(0);
1324  univ_fst_.SetFinal(0);
1325  for (int i = 0; i < kNumRandomLabels; ++i) univ_fst_.EmplaceArc(0, i, i, 0);
1326 
1327  weighted_tester_.reset(new WeightedTester<Arc>(seed, zero_fst_, one_fst_,
1328  univ_fst_, generate_));
1329 
1330  unweighted_tester_.reset(
1331  new UnweightedTester<Arc>(zero_fst_, one_fst_, univ_fst_, seed));
1332  }
1333 
1335  RandFst<Arc, WeightGenerator>(kNumRandomStates, kNumRandomArcs,
1336  kNumRandomLabels, kAcyclicProb, generate_,
1337  rand_(), fst);
1338  }
1339 
1340  void Test() {
1341  VLOG(1) << "weight type = " << Weight::Type();
1342 
1343  for (int i = 0; i < FST_FLAGS_repeat; ++i) {
1344  // Random transducers
1345  VectorFst<Arc> T1;
1346  VectorFst<Arc> T2;
1347  VectorFst<Arc> T3;
1348  MakeRandFst(&T1);
1349  MakeRandFst(&T2);
1350  MakeRandFst(&T3);
1351  weighted_tester_->Test(T1, T2, T3);
1352 
1353  VectorFst<Arc> A1(T1);
1354  VectorFst<Arc> A2(T2);
1355  VectorFst<Arc> A3(T3);
1359  ArcMap(&A1, rm_weight_mapper_);
1360  ArcMap(&A2, rm_weight_mapper_);
1361  ArcMap(&A3, rm_weight_mapper_);
1362  unweighted_tester_->Test(A1, A2, A3);
1363  }
1364  }
1365 
1366  private:
1367  // Generates weights used in testing.
1368  WeightGenerator generate_;
1369  // Random state used to seed RandFst.
1370  std::mt19937_64 rand_;
1371  // FST with no states
1372  VectorFst<Arc> zero_fst_;
1373  // FST with one state that accepts epsilon.
1374  VectorFst<Arc> one_fst_;
1375  // FST with one state that accepts all strings.
1376  VectorFst<Arc> univ_fst_;
1377  // Tests weighted FSTs
1378  std::unique_ptr<WeightedTester<Arc>> weighted_tester_;
1379  // Tests unweighted FSTs
1380  std::unique_ptr<UnweightedTester<Arc>> unweighted_tester_;
1381  // Mapper to remove weights from an Fst
1382  RmWeightMapper<Arc> rm_weight_mapper_;
1383  // Maximum number of states in random test Fst.
1384  static constexpr int kNumRandomStates = 10;
1385  // Maximum number of arcs in random test Fst.
1386  static constexpr int kNumRandomArcs = 25;
1387  // Number of alternative random labels.
1388  static constexpr int kNumRandomLabels = 5;
1389  // Probability to force an acyclic Fst
1390  static constexpr float kAcyclicProb = .25;
1391  // Maximum random path length.
1392  static constexpr int kRandomPathLength = 25;
1393  // Number of random paths to explore.
1394  static constexpr int kNumRandomPaths = 100;
1395 
1396  AlgoTester(const AlgoTester &) = delete;
1397  AlgoTester &operator=(const AlgoTester &) = delete;
1398 };
1399 } // namespace fst
1400 
1401 #endif // FST_TEST_ALGO_TEST_H_
MapSymbolsAction
Definition: arc-map.h:55
constexpr uint64_t kSemiring
Definition: weight.h:138
void ArcMap(MutableFst< A > *fst, C *mapper)
Definition: arc-map.h:110
void AddArc(StateId s, const Arc &arc) override
Definition: mutable-fst.h:326
bool RandEquivalent(const Fst< Arc > &fst1, const Fst< Arc > &fst2, int32_t npath, const RandGenOptions< ArcSelector > &opts, float delta=kDelta, uint64_t seed=std::random_device()(), bool *error=nullptr)
void SetStart(StateId s) override
Definition: mutable-fst.h:298
void RmEpsilon(MutableFst< Arc > *fst, std::vector< typename Arc::Weight > *distance, const RmEpsilonOptions< Arc, Queue > &opts)
Definition: rmepsilon.h:207
void Invert(const Fst< Arc > &ifst, MutableFst< Arc > *ofst)
Definition: invert.h:67
W Weight
Definition: arc.h:46
int Label
Definition: arc.h:47
virtual uint64_t Properties(uint64_t mask, bool test) const =0
constexpr uint64_t kOEpsilons
Definition: properties.h:88
void Encode(MutableFst< Arc > *fst, EncodeMapper< Arc > *mapper)
Definition: encode.h:474
constexpr uint8_t kPushLabels
Definition: push.h:107
void MakeRandFst(MutableFst< Arc > *fst)
Definition: algo_test.h:1334
void SetFinal(StateId s, Weight weight=Weight::One()) override
Definition: mutable-fst.h:303
ErrorWeight Plus(const ErrorWeight &, const ErrorWeight &)
Definition: error-weight.h:60
ArcMapFst(const Fst< typename ArcMapper::FromArc > &, const ArcMapper &) -> ArcMapFst< typename ArcMapper::FromArc, typename ArcMapper::ToArc, ArcMapper >
void Closure(MutableFst< Arc > *fst, ClosureType closure_type)
Definition: closure.h:47
static void Relabel(MutableFst< Arc > *fst, const LFST &mfst, bool relabel_input)
DECLARE_int32(repeat)
void Determinize(const Fst< Arc > &ifst, MutableFst< Arc > *ofst, const DeterminizeOptions< Arc > &opts=DeterminizeOptions< Arc >())
Definition: determinize.h:1079
ErrorWeight Times(const ErrorWeight &, const ErrorWeight &)
Definition: error-weight.h:63
typename Arc::Label Label
Definition: algo_test.h:94
void DfsVisit(const FST &fst, Visitor *visitor, ArcFilter filter, bool access_only=false)
Definition: dfs-visit.h:109
#define LOG(type)
Definition: log.h:49
StateId Start() const override
Definition: fst.h:950
typename Arc::StateId StateId
Definition: algo_test.h:1312
static const std::string & Type()
Definition: arc.h:68
bool TopSort(MutableFst< Arc > *fst)
Definition: topsort.h:90
ArcTpl< TropicalWeight > StdArc
Definition: arc.h:75
constexpr uint64_t kIdempotent
Definition: weight.h:144
void Connect(MutableFst< Arc > *fst)
Definition: connect.h:278
constexpr uint64_t kEpsilons
Definition: properties.h:78
DeterminizeType type
Definition: determinize.h:1044
constexpr uint64_t kNotOLabelSorted
Definition: properties.h:100
constexpr int kNoStateId
Definition: fst.h:202
typename Arc::StateId StateId
Definition: algo_test.h:95
const Arc & Value() const
Definition: fst.h:537
void Relabel(MutableFst< Arc > *fst, const std::vector< std::pair< typename Arc::Label, typename Arc::Label >> &ipairs, const std::vector< std::pair< typename Arc::Label, typename Arc::Label >> &opairs)
Definition: relabel.h:42
void Reverse(const Fst< Arc > &ifst, const std::vector< std::pair< typename Arc::Label, typename Arc::Label >> &parens, std::vector< typename Arc::Label > *assignments, MutableFst< RevArc > *ofst)
Definition: reverse.h:34
void Prune(MutableFst< Arc > *fst, const PruneOptions< Arc, ArcFilter > &opts=PruneOptions< Arc, ArcFilter >())
Definition: prune.h:111
constexpr uint64_t kRightSemiring
Definition: weight.h:136
void Difference(const Fst< Arc > &ifst1, const Fst< Arc > &ifst2, MutableFst< Arc > *ofst, const DifferenceOptions &opts=DifferenceOptions())
Definition: difference.h:165
StateId AddState() override
Definition: mutable-fst.h:316
void Test(const Fst< Arc > &A1, const Fst< Arc > &A2, const Fst< Arc > &A3)
Definition: algo_test.h:1040
MapFinalAction
Definition: arc-map.h:40
int StateId
Definition: arc.h:48
AlgoTester(WeightGenerator generator, uint64_t seed)
Definition: algo_test.h:1316
constexpr uint8_t kPushWeights
Definition: push.h:106
constexpr uint64_t kNotILabelSorted
Definition: properties.h:95
void RandGen(const Fst< FromArc > &ifst, MutableFst< ToArc > *ofst, const RandGenOptions< Selector > &opts)
Definition: randgen.h:752
MapSymbolsAction OutputSymbolsAction() const
Definition: algo_test.h:62
constexpr uint8_t kEncodeLabels
Definition: encode.h:43
constexpr uint64_t kNoOEpsilons
Definition: properties.h:90
void Union(RationalFst< Arc > *fst1, const Fst< Arc > &fst2)
Definition: union.h:110
void ArcSort(MutableFst< Arc > *fst, Compare comp)
Definition: arcsort.h:102
constexpr uint64_t kOLabelSorted
Definition: properties.h:98
constexpr uint64_t kNotAcceptor
Definition: properties.h:65
constexpr uint64_t kAcyclic
Definition: properties.h:110
constexpr uint64_t kCommutative
Definition: weight.h:141
constexpr uint64_t kNoEpsilons
Definition: properties.h:80
void Compose(const Fst< Arc > &ifst1, const Fst< Arc > &ifst2, MutableFst< Arc > *ofst, const ComposeOptions &opts=ComposeOptions())
Definition: compose.h:995
UnweightedTester(const Fst< Arc > &zero_fsa, const Fst< Arc > &one_fsa, const Fst< Arc > &univ_fsa, uint64_t seed)
Definition: algo_test.h:1037
StateId NumStates() const override
Definition: expanded-fst.h:134
MapFinalAction FinalAction() const
Definition: algo_test.h:58
void LookAheadCompose(const Fst< Arc > &ifst1, const Fst< Arc > &ifst2, MutableFst< Arc > *ofst)
Definition: algo_test.h:67
A operator()(const A &arc) const
Definition: algo_test.h:44
#define VLOG(level)
Definition: log.h:50
bool Done() const
Definition: fst.h:533
constexpr uint64_t kIDeterministic
Definition: properties.h:68
void ShortestPath(const Fst< Arc > &ifst, const std::vector< std::pair< typename Arc::Label, typename Arc::Label >> &parens, MutableFst< Arc > *ofst, const PdtShortestPathOptions< Arc, Queue > &opts)
constexpr uint64_t kIEpsilons
Definition: properties.h:83
MapSymbolsAction InputSymbolsAction() const
Definition: algo_test.h:60
void Concat(MutableFst< Arc > *fst1, const Fst< Arc > &fst2)
Definition: concat.h:48
void Project(const Fst< Arc > &ifst, MutableFst< Arc > *ofst, ProjectType project_type)
Definition: project.h:84
bool Verify(const Fst< Arc > &fst, bool allow_negative_labels=false)
Definition: verify.h:35
constexpr uint64_t kPath
Definition: weight.h:147
constexpr uint8_t kEncodeWeights
Definition: encode.h:44
constexpr uint64_t kILabelSorted
Definition: properties.h:93
constexpr uint64_t kFstProperties
Definition: properties.h:325
void Minimize(MutableFst< Arc > *fst, MutableFst< Arc > *sfst=nullptr, float delta=kShortestDelta, bool allow_nondet=false)
Definition: minimize.h:504
uint64_t Properties(uint64_t props) const
Definition: algo_test.h:48
WeightedTester(uint64_t seed, const Fst< Arc > &zero_fst, const Fst< Arc > &one_fst, const Fst< Arc > &univ_fst, WeightGenerator weight_generator)
Definition: algo_test.h:99
typename Arc::Label Label
Definition: algo_test.h:1311
typename Arc::Weight Weight
Definition: algo_test.h:1313
#define CHECK_EQ(x, y)
Definition: log.h:62
void Test(const Fst< Arc > &A1, const Fst< Arc > &A2, const Fst< Arc > &A3)
Definition: algo_test.h:1061
bool Equivalent(const Fst< Arc > &fst1, const Fst< Arc > &fst2, float delta=kDelta, bool *error=nullptr)
Definition: equivalent.h:121
void Test(const Fst< Arc > &T1, const Fst< Arc > &T2, const Fst< Arc > &T3)
Definition: algo_test.h:109
void ShortestDistance(const Fst< Arc > &fst, std::vector< typename Arc::Weight > *distance, const ShortestDistanceOptions< Arc, Queue, ArcFilter > &opts)
#define CHECK(x)
Definition: log.h:61
UnweightedTester(const Fst< Arc > &zero_fsa, const Fst< Arc > &one_fsa, const Fst< Arc > &univ_fsa, uint64_t seed)
Definition: algo_test.h:1054
constexpr uint64_t kLeftSemiring
Definition: weight.h:133
constexpr float kDelta
Definition: weight.h:130
void Disambiguate(const Fst< Arc > &ifst, MutableFst< Arc > *ofst, const DisambiguateOptions< Arc > &opts=DisambiguateOptions< Arc >())
Definition: disambiguate.h:573
typename Arc::Weight Weight
Definition: algo_test.h:96
constexpr uint64_t kNoIEpsilons
Definition: properties.h:85
void Decode(MutableFst< Arc > *fst, const EncodeMapper< Arc > &mapper)
Definition: encode.h:481
std::bool_constant<(W::Properties()&kPath)!=0 > IsPath
Definition: weight.h:159
void Reweight(MutableFst< Arc > *fst, const std::vector< typename Arc::Weight > &potential, ReweightType type)
Definition: reweight.h:45
bool ApproxEqual(const ErrorWeight &, const ErrorWeight &, float)
Definition: error-weight.h:57
uint64_t Properties(uint64_t mask, bool test) const override
Definition: fst.h:966
constexpr uint64_t kAcceptor
Definition: properties.h:63
void Next()
Definition: fst.h:541