HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Jelly Bean
|
4.1.1_r1
下载
查看原文件
收藏
根目录
external
openfst
src
test
algo_test.h
// algo_test.h // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Copyright 2005-2010 Google, Inc. // Author: riley@google.com (Michael Riley) // // \file // Regression test for various FST algorithms. #ifndef FST_TEST_ALGO_TEST_H__ #define FST_TEST_ALGO_TEST_H__ #include
#include
DECLARE_int32(repeat); // defined in ./algo_test.cc namespace fst { // Mapper to change input and output label of every transition into // epsilons. template
class EpsMapper { public: EpsMapper() {} A operator()(const A &arc) const { return A(0, 0, arc.weight, arc.nextstate); } uint64 Properties(uint64 props) const { props &= ~kNotAcceptor; props |= kAcceptor; props &= ~kNoIEpsilons & ~kNoOEpsilons & ~kNoEpsilons; props |= kIEpsilons | kOEpsilons | kEpsilons; props &= ~kNotILabelSorted & ~kNotOLabelSorted; props |= kILabelSorted | kOLabelSorted; return props; } MapFinalAction FinalAction() const { return MAP_NO_SUPERFINAL; } MapSymbolsAction InputSymbolsAction() const { return MAP_COPY_SYMBOLS;} MapSymbolsAction OutputSymbolsAction() const { return MAP_COPY_SYMBOLS;} }; // This class tests a variety of identities and properties that must // hold for various algorithms on weighted FSTs. template
class WeightedTester { public: typedef typename Arc::Label Label; typedef typename Arc::StateId StateId; typedef typename Arc::Weight Weight; WeightedTester(int seed, const Fst
&zero_fst, const Fst
&one_fst, const Fst
&univ_fst, WeightGenerator *weight_generator) : seed_(seed), zero_fst_(zero_fst), one_fst_(one_fst), univ_fst_(univ_fst), weight_generator_(weight_generator) {} void Test(const Fst
&T1, const Fst
&T2, const Fst
&T3) { TestRational(T1, T2, T3); TestMap(T1); TestCompose(T1, T2, T3); TestSort(T1); TestOptimize(T1); TestSearch(T1); } private: // Tests rational operations with identities void TestRational(const Fst
&T1, const Fst
&T2, const Fst
&T3) { { VLOG(1) << "Check destructive and delayed union are equivalent."; VectorFst
U1(T1); Union(&U1, T2); UnionFst
U2(T1, T2); CHECK(Equiv(U1, U2)); } { VLOG(1) << "Check destructive and delayed concatenation are equivalent."; VectorFst
C1(T1); Concat(&C1, T2); ConcatFst
C2(T1, T2); CHECK(Equiv(C1, C2)); VectorFst
C3(T2); Concat(T1, &C3); CHECK(Equiv(C3, C2)); } { VLOG(1) << "Check destructive and delayed closure* are equivalent."; VectorFst
C1(T1); Closure(&C1, CLOSURE_STAR); ClosureFst
C2(T1, CLOSURE_STAR); CHECK(Equiv(C1, C2)); } { VLOG(1) << "Check destructive and delayed closure+ are equivalent."; VectorFst
C1(T1); Closure(&C1, CLOSURE_PLUS); ClosureFst
C2(T1, CLOSURE_PLUS); CHECK(Equiv(C1, C2)); } { VLOG(1) << "Check union is associative (destructive)."; VectorFst
U1(T1); Union(&U1, T2); Union(&U1, T3); VectorFst
U3(T2); Union(&U3, T3); VectorFst
U4(T1); Union(&U4, U3); CHECK(Equiv(U1, U4)); } { VLOG(1) << "Check union is associative (delayed)."; UnionFst
U1(T1, T2); UnionFst
U2(U1, T3); UnionFst
U3(T2, T3); UnionFst
U4(T1, U3); CHECK(Equiv(U2, U4)); } { VLOG(1) << "Check union is associative (destructive delayed)."; UnionFst
U1(T1, T2); Union(&U1, T3); UnionFst
U3(T2, T3); UnionFst
U4(T1, U3); CHECK(Equiv(U1, U4)); } { VLOG(1) << "Check concatenation is associative (destructive)."; VectorFst
C1(T1); Concat(&C1, T2); Concat(&C1, T3); VectorFst
C3(T2); Concat(&C3, T3); VectorFst
C4(T1); Concat(&C4, C3); CHECK(Equiv(C1, C4)); } { VLOG(1) << "Check concatenation is associative (delayed)."; ConcatFst
C1(T1, T2); ConcatFst
C2(C1, T3); ConcatFst
C3(T2, T3); ConcatFst
C4(T1, C3); CHECK(Equiv(C2, C4)); } { VLOG(1) << "Check concatenation is associative (destructive delayed)."; ConcatFst
C1(T1, T2); Concat(&C1, T3); ConcatFst
C3(T2, T3); ConcatFst
C4(T1, C3); CHECK(Equiv(C1, C4)); } if (Weight::Properties() & kLeftSemiring) { VLOG(1) << "Check concatenation left distributes" << " over union (destructive)."; VectorFst
U1(T1); Union(&U1, T2); VectorFst
C1(T3); Concat(&C1, U1); VectorFst
C2(T3); Concat(&C2, T1); VectorFst
C3(T3); Concat(&C3, T2); VectorFst
U2(C2); Union(&U2, C3); CHECK(Equiv(C1, U2)); } if (Weight::Properties() & kRightSemiring) { VLOG(1) << "Check concatenation right distributes" << " over union (destructive)."; VectorFst
U1(T1); Union(&U1, T2); VectorFst
C1(U1); Concat(&C1, T3); VectorFst
C2(T1); Concat(&C2, T3); VectorFst
C3(T2); Concat(&C3, T3); VectorFst
U2(C2); Union(&U2, C3); CHECK(Equiv(C1, U2)); } if (Weight::Properties() & kLeftSemiring) { VLOG(1) << "Check concatenation left distributes over union (delayed)."; UnionFst
U1(T1, T2); ConcatFst
C1(T3, U1); ConcatFst
C2(T3, T1); ConcatFst
C3(T3, T2); UnionFst
U2(C2, C3); CHECK(Equiv(C1, U2)); } if (Weight::Properties() & kRightSemiring) { VLOG(1) << "Check concatenation right distributes over union (delayed)."; UnionFst
U1(T1, T2); ConcatFst
C1(U1, T3); ConcatFst
C2(T1, T3); ConcatFst
C3(T2, T3); UnionFst
U2(C2, C3); CHECK(Equiv(C1, U2)); } if (Weight::Properties() & kLeftSemiring) { VLOG(1) << "Check T T* == T+ (destructive)."; VectorFst
S(T1); Closure(&S, CLOSURE_STAR); VectorFst
C(T1); Concat(&C, S); VectorFst
P(T1); Closure(&P, CLOSURE_PLUS); CHECK(Equiv(C, P)); } if (Weight::Properties() & kRightSemiring) { VLOG(1) << "Check T* T == T+ (destructive)."; VectorFst
S(T1); Closure(&S, CLOSURE_STAR); VectorFst
C(S); Concat(&C, T1); VectorFst
P(T1); Closure(&P, CLOSURE_PLUS); CHECK(Equiv(C, P)); } if (Weight::Properties() & kLeftSemiring) { VLOG(1) << "Check T T* == T+ (delayed)."; ClosureFst
S(T1, CLOSURE_STAR); ConcatFst
C(T1, S); ClosureFst
P(T1, CLOSURE_PLUS); CHECK(Equiv(C, P)); } if (Weight::Properties() & kRightSemiring) { VLOG(1) << "Check T* T == T+ (delayed)."; ClosureFst
S(T1, CLOSURE_STAR); ConcatFst
C(S, T1); ClosureFst
P(T1, CLOSURE_PLUS); CHECK(Equiv(C, P)); } } // Tests map-based operations. void TestMap(const Fst
&T) { { VLOG(1) << "Check destructive and delayed projection are equivalent."; VectorFst
P1(T); Project(&P1, PROJECT_INPUT); ProjectFst
P2(T, PROJECT_INPUT); CHECK(Equiv(P1, P2)); } { VLOG(1) << "Check destructive and delayed inversion are equivalent."; VectorFst
I1(T); Invert(&I1); InvertFst
I2(T); CHECK(Equiv(I1, I2)); } { VLOG(1) << "Check Pi_1(T) = Pi_2(T^-1) (destructive)."; VectorFst
P1(T); VectorFst
I1(T); Project(&P1, PROJECT_INPUT); Invert(&I1); Project(&I1, PROJECT_OUTPUT); CHECK(Equiv(P1, I1)); } { VLOG(1) << "Check Pi_2(T) = Pi_1(T^-1) (destructive)."; VectorFst
P1(T); VectorFst
I1(T); Project(&P1, PROJECT_OUTPUT); Invert(&I1); Project(&I1, PROJECT_INPUT); CHECK(Equiv(P1, I1)); } { VLOG(1) << "Check Pi_1(T) = Pi_2(T^-1) (delayed)."; ProjectFst
P1(T, PROJECT_INPUT); InvertFst
I1(T); ProjectFst
P2(I1, PROJECT_OUTPUT); CHECK(Equiv(P1, P2)); } { VLOG(1) << "Check Pi_2(T) = Pi_1(T^-1) (delayed)."; ProjectFst
P1(T, PROJECT_OUTPUT); InvertFst
I1(T); ProjectFst
P2(I1, PROJECT_INPUT); CHECK(Equiv(P1, P2)); } { VLOG(1) << "Check destructive relabeling"; static const int kNumLabels = 10; // set up relabeling pairs vector
labelset(kNumLabels); for (size_t i = 0; i < kNumLabels; ++i) labelset[i] = i; for (size_t i = 0; i < kNumLabels; ++i) { swap(labelset[i], labelset[rand() % kNumLabels]); } vector
> ipairs1(kNumLabels); vector
> opairs1(kNumLabels); for (size_t i = 0; i < kNumLabels; ++i) { ipairs1[i] = make_pair(i, labelset[i]); opairs1[i] = make_pair(labelset[i], i); } VectorFst
R(T); Relabel(&R, ipairs1, opairs1); vector
> ipairs2(kNumLabels); vector
> opairs2(kNumLabels); for (size_t i = 0; i < kNumLabels; ++i) { ipairs2[i] = make_pair(labelset[i], i); opairs2[i] = make_pair(i, labelset[i]); } Relabel(&R, ipairs2, opairs2); CHECK(Equiv(R, T)); VLOG(1) << "Check on-the-fly relabeling"; RelabelFst
Rdelay(T, ipairs1, opairs1); RelabelFst
RRdelay(Rdelay, ipairs2, opairs2); CHECK(Equiv(RRdelay, T)); } { VLOG(1) << "Check encoding/decoding (destructive)."; VectorFst
D(T); uint32 encode_props = 0; if (rand() % 2) encode_props |= kEncodeLabels; if (rand() % 2) encode_props |= kEncodeWeights; EncodeMapper
encoder(encode_props, ENCODE); Encode(&D, &encoder); Decode(&D, encoder); CHECK(Equiv(D, T)); } { VLOG(1) << "Check encoding/decoding (delayed)."; uint32 encode_props = 0; if (rand() % 2) encode_props |= kEncodeLabels; if (rand() % 2) encode_props |= kEncodeWeights; EncodeMapper
encoder(encode_props, ENCODE); EncodeFst
E(T, &encoder); VectorFst
Encoded(E); DecodeFst
D(Encoded, encoder); CHECK(Equiv(D, T)); } { VLOG(1) << "Check gallic mappers (constructive)."; ToGallicMapper
to_mapper; FromGallicMapper
from_mapper; VectorFst< GallicArc
> G; VectorFst
F; ArcMap(T, &G, to_mapper); ArcMap(G, &F, from_mapper); CHECK(Equiv(T, F)); } { VLOG(1) << "Check gallic mappers (delayed)."; ToGallicMapper
to_mapper; FromGallicMapper
from_mapper; ArcMapFst
, ToGallicMapper
> G(T, to_mapper); ArcMapFst
, Arc, FromGallicMapper
> F(G, from_mapper); CHECK(Equiv(T, F)); } } // Tests compose-based operations. void TestCompose(const Fst
&T1, const Fst
&T2, const Fst
&T3) { if (!(Weight::Properties() & kCommutative)) return; VectorFst
S1(T1); VectorFst
S2(T2); VectorFst
S3(T3); ILabelCompare
icomp; OLabelCompare
ocomp; ArcSort(&S1, ocomp); ArcSort(&S2, ocomp); ArcSort(&S3, icomp); { VLOG(1) << "Check composition is associative."; ComposeFst
C1(S1, S2); ComposeFst
C2(C1, S3); ComposeFst
C3(S2, S3); ComposeFst
C4(S1, C3); CHECK(Equiv(C2, C4)); } { VLOG(1) << "Check composition left distributes over union."; UnionFst
U1(S2, S3); ComposeFst
C1(S1, U1); ComposeFst
C2(S1, S2); ComposeFst
C3(S1, S3); UnionFst
U2(C2, C3); CHECK(Equiv(C1, U2)); } { VLOG(1) << "Check composition right distributes over union."; UnionFst
U1(S1, S2); ComposeFst
C1(U1, S3); ComposeFst
C2(S1, S3); ComposeFst
C3(S2, S3); UnionFst
U2(C2, C3); CHECK(Equiv(C1, U2)); } VectorFst
A1(S1); VectorFst
A2(S2); VectorFst
A3(S3); Project(&A1, PROJECT_OUTPUT); Project(&A2, PROJECT_INPUT); Project(&A3, PROJECT_INPUT); { VLOG(1) << "Check intersection is commutative."; IntersectFst
I1(A1, A2); IntersectFst
I2(A2, A1); CHECK(Equiv(I1, I2)); } { VLOG(1) << "Check all epsilon filters leads to equivalent results."; typedef Matcher< Fst
> M; ComposeFst
C1(S1, S2); ComposeFst
C2( S1, S2, ComposeFstOptions
>()); ComposeFst
C3( S1, S2, ComposeFstOptions
>()); CHECK(Equiv(C1, C2)); CHECK(Equiv(C1, C3)); } } // Tests sorting operations void TestSort(const Fst
&T) { ILabelCompare
icomp; OLabelCompare
ocomp; { VLOG(1) << "Check arc sorted Fst is equivalent to its input."; VectorFst
S1(T); ArcSort(&S1, icomp); CHECK(Equiv(T, S1)); } { VLOG(1) << "Check destructive and delayed arcsort are equivalent."; VectorFst
S1(T); ArcSort(&S1, icomp); ArcSortFst< Arc, ILabelCompare
> S2(T, icomp); CHECK(Equiv(S1, S2)); } { VLOG(1) << "Check ilabel sorting vs. olabel sorting with inversions."; VectorFst
S1(T); VectorFst
S2(T); ArcSort(&S1, icomp); Invert(&S2); ArcSort(&S2, ocomp); Invert(&S2); CHECK(Equiv(S1, S2)); } { VLOG(1) << "Check topologically sorted Fst is equivalent to its input."; VectorFst
S1(T); TopSort(&S1); CHECK(Equiv(T, S1)); } { VLOG(1) << "Check reverse(reverse(T)) = T"; VectorFst< ReverseArc
> R1; VectorFst
R2; Reverse(T, &R1); Reverse(R1, &R2); CHECK(Equiv(T, R2)); } } // Tests optimization operations void TestOptimize(const Fst
&T) { uint64 tprops = T.Properties(kFstProperties, true); uint64 wprops = Weight::Properties(); VectorFst
A(T); Project(&A, PROJECT_INPUT); { VLOG(1) << "Check connected FST is equivalent to its input."; VectorFst
C1(T); Connect(&C1); CHECK(Equiv(T, C1)); } if ((wprops & kSemiring) == kSemiring && (tprops & kAcyclic || wprops & kIdempotent)) { VLOG(1) << "Check epsilon-removed FST is equivalent to its input."; VectorFst
R1(T); RmEpsilon(&R1); CHECK(Equiv(T, R1)); VLOG(1) << "Check destructive and delayed epsilon removal" << "are equivalent."; RmEpsilonFst
R2(T); CHECK(Equiv(R1, R2)); VLOG(1) << "Check an FST with a large proportion" << " of epsilon transitions:"; // Maps all transitions of T to epsilon-transitions and append // a non-epsilon transition. VectorFst
U; ArcMap(T, &U, EpsMapper
()); VectorFst
V; V.SetStart(V.AddState()); Arc arc(1, 1, Weight::One(), V.AddState()); V.AddArc(V.Start(), arc); V.SetFinal(arc.nextstate, Weight::One()); Concat(&U, V); // Check that epsilon-removal preserves the shortest-distance // from the initial state to the final states. vector
d; ShortestDistance(U, &d, true); Weight w = U.Start() < d.size() ? d[U.Start()] : Weight::Zero(); VectorFst
U1(U); RmEpsilon(&U1); ShortestDistance(U1, &d, true); Weight w1 = U1.Start() < d.size() ? d[U1.Start()] : Weight::Zero(); CHECK(ApproxEqual(w, w1, kTestDelta)); RmEpsilonFst
U2(U); ShortestDistance(U2, &d, true); Weight w2 = U2.Start() < d.size() ? d[U2.Start()] : Weight::Zero(); CHECK(ApproxEqual(w, w2, kTestDelta)); } if ((wprops & kSemiring) == kSemiring && tprops & kAcyclic) { VLOG(1) << "Check determinized FSA is equivalent to its input."; DeterminizeFst
D(A); CHECK(Equiv(A, D)); int n; { VLOG(1) << "Check size(min(det(A))) <= size(det(A))" << " and min(det(A)) equiv det(A)"; VectorFst
M(D); n = M.NumStates(); Minimize(&M); CHECK(Equiv(D, M)); CHECK(M.NumStates() <= n); n = M.NumStates(); } if (n && (wprops & kIdempotent) == kIdempotent && A.Properties(kNoEpsilons, true)) { VLOG(1) << "Check that Revuz's algorithm leads to the" << " same number of states as Brozozowski's algorithm"; // Skip test if A is the empty machine or contains epsilons or // if the semiring is not idempotent (to avoid floating point // errors) VectorFst
R; Reverse(A, &R); RmEpsilon(&R); DeterminizeFst
DR(R); VectorFst
RD; Reverse(DR, &RD); DeterminizeFst
DRD(RD); VectorFst
M(DRD); CHECK_EQ(n + 1, M.NumStates()); // Accounts for the epsilon transition // to the initial state } } if (Arc::Type() == LogArc::Type() || Arc::Type() == StdArc::Type()) { VLOG(1) << "Check reweight(T) equiv T"; vector
potential; VectorFst
RI(T); VectorFst
RF(T); while (potential.size() < RI.NumStates()) potential.push_back((*weight_generator_)()); Reweight(&RI, potential, REWEIGHT_TO_INITIAL); CHECK(Equiv(T, RI)); Reweight(&RF, potential, REWEIGHT_TO_FINAL); CHECK(Equiv(T, RF)); } if ((wprops & kIdempotent) || (tprops & kAcyclic)) { VLOG(1) << "Check pushed FST is equivalent to input FST."; // Pushing towards the final state. if (wprops & kRightSemiring) { VectorFst
P1; Push
(T, &P1, kPushLabels); CHECK(Equiv(T, P1)); VectorFst
P2; Push
(T, &P2, kPushWeights); CHECK(Equiv(T, P2)); VectorFst
P3; Push
(T, &P3, kPushLabels | kPushWeights); CHECK(Equiv(T, P3)); } // Pushing towards the initial state. if (wprops & kLeftSemiring) { VectorFst
P1; Push
(T, &P1, kPushLabels); CHECK(Equiv(T, P1)); VectorFst
P2; Push
(T, &P2, kPushWeights); CHECK(Equiv(T, P2)); VectorFst
P3; Push
(T, &P3, kPushLabels | kPushWeights); CHECK(Equiv(T, P3)); } } if ((wprops & (kPath | kCommutative)) == (kPath | kCommutative)) { VLOG(1) << "Check pruning algorithm"; { VLOG(1) << "Check equiv. of constructive and destructive algorithms"; Weight thresold = (*weight_generator_)(); VectorFst
P1(T); Prune(&P1, thresold); VectorFst
P2; Prune(T, &P2, thresold); CHECK(Equiv(P1,P2)); } { VLOG(1) << "Check prune(reverse) equiv reverse(prune)"; Weight thresold = (*weight_generator_)(); VectorFst< ReverseArc
> R; VectorFst
P1(T); VectorFst
P2; Prune(&P1, thresold); Reverse(T, &R); Prune(&R, thresold.Reverse()); Reverse(R, &P2); CHECK(Equiv(P1, P2)); } { VLOG(1) << "Check: ShortestDistance(T- prune(T))" << " > ShortestDistance(T) times Thresold"; Weight thresold = (*weight_generator_)(); VectorFst
P; Prune(A, &P, thresold); DifferenceFst
C(A, DeterminizeFst
(RmEpsilonFst
(ArcMapFst
> (P, RmWeightMapper
())))); Weight sum1 = Times(ShortestDistance(A), thresold); Weight sum2 = ShortestDistance(C); CHECK(Plus(sum1, sum2) == sum1); } } if (tprops & kAcyclic) { VLOG(1) << "Check synchronize(T) equiv T"; SynchronizeFst
S(T); CHECK(Equiv(T, S)); } } // Tests search operations void TestSearch(const Fst
&T) { uint64 wprops = Weight::Properties(); VectorFst
A(T); Project(&A, PROJECT_INPUT); if ((wprops & (kPath | kRightSemiring)) == (kPath | kRightSemiring)) { VLOG(1) << "Check 1-best weight."; VectorFst
path; ShortestPath(T, &path); Weight tsum = ShortestDistance(T); Weight psum = ShortestDistance(path); CHECK(ApproxEqual(tsum, psum, kTestDelta)); } if ((wprops & (kPath | kSemiring)) == (kPath | kSemiring)) { VLOG(1) << "Check n-best weights"; VectorFst
R(A); RmEpsilon(&R); int nshortest = rand() % kNumRandomShortestPaths + 2; VectorFst
paths; ShortestPath(R, &paths, nshortest, true, false, Weight::Zero(), kNumShortestStates); vector
distance; ShortestDistance(paths, &distance, true); StateId pstart = paths.Start(); if (pstart != kNoStateId) { ArcIterator< Fst
> piter(paths, pstart); for (; !piter.Done(); piter.Next()) { StateId s = piter.Value().nextstate; Weight nsum = s < distance.size() ? Times(piter.Value().weight, distance[s]) : Weight::Zero(); VectorFst
path; ShortestPath(R, &path); Weight dsum = ShortestDistance(path); CHECK(ApproxEqual(nsum, dsum, kTestDelta)); ArcMap(&path, RmWeightMapper
()); VectorFst
S; Difference(R, path, &S); R = S; } } } } // Tests if two FSTS are equivalent by checking if random // strings from one FST are transduced the same by both FSTs. bool Equiv(const Fst
&fst1, const Fst
&fst2) { VLOG(1) << "Check FSTs for sanity (including property bits)."; CHECK(Verify(fst1)); CHECK(Verify(fst2)); UniformArcSelector
uniform_selector(seed_); RandGenOptions< UniformArcSelector
> opts(uniform_selector, kRandomPathLength); return RandEquivalent(fst1, fst2, kNumRandomPaths, kTestDelta, opts); } // Random seed int seed_; // FST with no states VectorFst
zero_fst_; // FST with one state that accepts epsilon. VectorFst
one_fst_; // FST with one state that accepts all strings. VectorFst
univ_fst_; // Generates weights used in testing. WeightGenerator *weight_generator_; // Maximum random path length. static const int kRandomPathLength; // Number of random paths to explore. static const int kNumRandomPaths; // Maximum number of nshortest paths. static const int kNumRandomShortestPaths; // Maximum number of nshortest states. static const int kNumShortestStates; // Delta for equivalence tests. static const float kTestDelta; DISALLOW_COPY_AND_ASSIGN(WeightedTester); }; template
const int WeightedTester
::kRandomPathLength = 25; template
const int WeightedTester
::kNumRandomPaths = 100; template
const int WeightedTester
::kNumRandomShortestPaths = 100; template
const int WeightedTester
::kNumShortestStates = 10000; template
const float WeightedTester
::kTestDelta = .05; // This class tests a variety of identities and properties that must // hold for various algorithms on unweighted FSAs and that are not tested // by WeightedTester. Only the specialization does anything interesting. template
class UnweightedTester { public: UnweightedTester(const Fst
&zero_fsa, const Fst
&one_fsa, const Fst
&univ_fsa) {} void Test(const Fst
&A1, const Fst
&A2, const Fst
&A3) {} }; // Specialization for StdArc. This should work for any commutative, // idempotent semiring when restricted to the unweighted case // (being isomorphic to the boolean semiring). template <> class UnweightedTester
{ public: typedef StdArc Arc; typedef Arc::Label Label; typedef Arc::StateId StateId; typedef Arc::Weight Weight; UnweightedTester(const Fst
&zero_fsa, const Fst
&one_fsa, const Fst
&univ_fsa) : zero_fsa_(zero_fsa), one_fsa_(one_fsa), univ_fsa_(univ_fsa) {} void Test(const Fst
&A1, const Fst
&A2, const Fst
&A3) { TestRational(A1, A2, A3); TestIntersect(A1, A2, A3); TestOptimize(A1); } private: // Tests rational operations with identities void TestRational(const Fst
&A1, const Fst
&A2, const Fst
&A3) { { VLOG(1) << "Check the union contains its arguments (destructive)."; VectorFst
U(A1); Union(&U, A2); CHECK(Subset(A1, U)); CHECK(Subset(A2, U)); } { VLOG(1) << "Check the union contains its arguments (delayed)."; UnionFst
U(A1, A2); CHECK(Subset(A1, U)); CHECK(Subset(A2, U)); } { VLOG(1) << "Check if A^n c A* (destructive)."; VectorFst
C(one_fsa_); int n = rand() % 5; for (int i = 0; i < n; ++i) Concat(&C, A1); VectorFst
S(A1); Closure(&S, CLOSURE_STAR); CHECK(Subset(C, S)); } { VLOG(1) << "Check if A^n c A* (delayed)."; int n = rand() % 5; Fst
*C = new VectorFst
(one_fsa_); for (int i = 0; i < n; ++i) { ConcatFst
*F = new ConcatFst