• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // weight-tester.h
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 // Copyright 2005-2010 Google, Inc.
16 // Author: riley@google.com (Michael Riley)
17 //
18 // \file
19 // Utility class for regression testing of Fst weights.
20 
21 #ifndef FST_TEST_WEIGHT_TESTER_H_
22 #define FST_TEST_WEIGHT_TESTER_H_
23 
24 #include <iostream>
25 #include <sstream>
26 
27 #include <fst/random-weight.h>
28 
29 namespace fst {
30 
31 // This class tests a variety of identities and properties that must
32 // hold for the Weight class to be well-defined. It calls function object
33 // WEIGHT_GENERATOR to select weights that are used in the tests.
34 template<class Weight, class WeightGenerator>
35 class WeightTester {
36  public:
WeightTester(WeightGenerator generator)37   WeightTester(WeightGenerator generator) : weight_generator_(generator) {}
38 
39   void Test(int iterations, bool test_division = true) {
40     for (int i = 0; i < iterations; ++i) {
41       // Selects the test weights.
42       Weight w1 = weight_generator_();
43       Weight w2 = weight_generator_();
44       Weight w3 = weight_generator_();
45 
46       VLOG(1) << "weight type = " << Weight::Type();
47       VLOG(1) << "w1 = " << w1;
48       VLOG(1) << "w2 = " << w2;
49       VLOG(1) << "w3 = " << w3;
50 
51       TestSemiring(w1, w2, w3);
52       if (test_division)
53         TestDivision(w1, w2);
54       TestReverse(w1, w2);
55       TestEquality(w1, w2, w3);
56       TestIO(w1);
57       TestCopy(w1);
58     }
59   }
60 
61  private:
62   // Note in the tests below we use ApproxEqual rather than == and add
63   // kDelta to inequalities where the weights might be inexact.
64 
65   // Tests (Plus, Times, Zero, One) defines a commutative semiring.
TestSemiring(Weight w1,Weight w2,Weight w3)66   void TestSemiring(Weight w1, Weight w2, Weight w3) {
67     // Checks that the operations are closed.
68     CHECK(Plus(w1, w2).Member());
69     CHECK(Times(w1, w2).Member());
70 
71     // Checks that the operations are associative.
72     CHECK(ApproxEqual(Plus(w1, Plus(w2, w3)), Plus(Plus(w1, w2), w3)));
73     CHECK(ApproxEqual(Times(w1, Times(w2, w3)), Times(Times(w1, w2), w3)));
74 
75     // Checks the identity elements.
76     CHECK(Plus(w1, Weight::Zero()) == w1);
77     CHECK(Plus(Weight::Zero(), w1) == w1);
78     CHECK(Times(w1, Weight::One()) == w1);
79     CHECK(Times(Weight::One(), w1) == w1);
80 
81     // Check the no weight element.
82     CHECK(!Weight::NoWeight().Member());
83     CHECK(!Plus(w1, Weight::NoWeight()).Member());
84     CHECK(!Plus(Weight::NoWeight(), w1).Member());
85     CHECK(!Times(w1, Weight::NoWeight()).Member());
86     CHECK(!Times(Weight::NoWeight(), w1).Member());
87 
88     // Checks that the operations commute.
89     CHECK(ApproxEqual(Plus(w1, w2), Plus(w2, w1)));
90     if (Weight::Properties() & kCommutative)
91       CHECK(ApproxEqual(Times(w1, w2), Times(w2, w1)));
92 
93     // Checks Zero() is the annihilator.
94     CHECK(Times(w1, Weight::Zero()) == Weight::Zero());
95     CHECK(Times(Weight::Zero(), w1) == Weight::Zero());
96 
97     // Check Power(w, 0) is Weight::One()
98     CHECK(Power(w1, 0) == Weight::One());
99 
100     // Check Power(w, 1) is w
101     CHECK(Power(w1, 1) == w1);
102 
103     // Check Power(w, 3) is Times(w, Times(w, w))
104     CHECK(Power(w1, 3) == Times(w1, Times(w1, w1)));
105 
106     // Checks distributivity.
107     if (Weight::Properties() & kLeftSemiring)
108       CHECK(ApproxEqual(Times(w1, Plus(w2, w3)),
109                         Plus(Times(w1, w2), Times(w1, w3))));
110     if (Weight::Properties() & kRightSemiring)
111       CHECK(ApproxEqual(Times(Plus(w1, w2), w3),
112                         Plus(Times(w1, w3), Times(w2, w3))));
113 
114     if (Weight::Properties() & kIdempotent)
115       CHECK(Plus(w1, w1) == w1);
116 
117     if (Weight::Properties() & kPath)
118       CHECK(Plus(w1, w2) == w1 || Plus(w1, w2) == w2);
119 
120     // Ensure weights form a left or right semiring.
121     CHECK(Weight::Properties() & (kLeftSemiring | kRightSemiring));
122 
123     // Check when Times() is commutative that it is marked as a semiring.
124     if (Weight::Properties() & kCommutative)
125       CHECK(Weight::Properties() & kSemiring);
126   }
127 
128   // Tests division operation.
TestDivision(Weight w1,Weight w2)129   void TestDivision(Weight w1, Weight w2) {
130     Weight p = Times(w1, w2);
131 
132     if (Weight::Properties() & kLeftSemiring) {
133       Weight d = Divide(p, w1, DIVIDE_LEFT);
134       if (d.Member())
135         CHECK(ApproxEqual(p, Times(w1, d)));
136       CHECK(!Divide(w1, Weight::NoWeight(), DIVIDE_LEFT).Member());
137       CHECK(!Divide(Weight::NoWeight(), w1, DIVIDE_LEFT).Member());
138     }
139 
140     if (Weight::Properties() & kRightSemiring) {
141       Weight d = Divide(p, w2, DIVIDE_RIGHT);
142       if (d.Member())
143         CHECK(ApproxEqual(p, Times(d, w2)));
144       CHECK(!Divide(w1, Weight::NoWeight(), DIVIDE_RIGHT).Member());
145       CHECK(!Divide(Weight::NoWeight(), w1, DIVIDE_RIGHT).Member());
146     }
147 
148     if (Weight::Properties() & kCommutative) {
149       Weight d = Divide(p, w1, DIVIDE_RIGHT);
150       if (d.Member())
151         CHECK(ApproxEqual(p, Times(d, w1)));
152     }
153   }
154 
155   // Tests reverse operation.
TestReverse(Weight w1,Weight w2)156   void TestReverse(Weight w1, Weight w2) {
157     typedef typename Weight::ReverseWeight ReverseWeight;
158 
159     ReverseWeight rw1 = w1.Reverse();
160     ReverseWeight rw2 = w2.Reverse();
161 
162     CHECK(rw1.Reverse() == w1);
163     CHECK(Plus(w1, w2).Reverse() == Plus(rw1, rw2));
164     CHECK(Times(w1, w2).Reverse() == Times(rw2, rw1));
165   }
166 
167   // Tests == is an equivalence relation.
TestEquality(Weight w1,Weight w2,Weight w3)168   void TestEquality(Weight w1, Weight w2, Weight w3) {
169     // Checks reflexivity.
170     CHECK(w1 == w1);
171 
172     // Checks symmetry.
173     CHECK((w1 == w2) == (w2 == w1));
174 
175     // Checks transitivity.
176     if (w1 == w2 && w2 == w3)
177       CHECK(w1 == w3);
178   }
179 
180   // Tests binary serialization and textual I/O.
TestIO(Weight w)181   void TestIO(Weight w) {
182     // Tests binary I/O
183     {
184     ostringstream os;
185     w.Write(os);
186     os.flush();
187     istringstream is(os.str());
188     Weight v;
189     v.Read(is);
190     CHECK_EQ(w, v);
191     }
192 
193     // Tests textual I/O.
194     {
195       ostringstream os;
196       os << w;
197       istringstream is(os.str());
198       Weight v(Weight::One());
199       is >> v;
200       CHECK(ApproxEqual(w, v));
201     }
202   }
203 
204   // Tests copy constructor and assignment operator
TestCopy(Weight w)205   void TestCopy(Weight w) {
206     Weight x = w;
207     CHECK(w == x);
208 
209     x = Weight(w);
210     CHECK(w == x);
211 
212     x.operator=(x);
213     CHECK(w == x);
214 
215   }
216 
217   // Generates weights used in testing.
218   WeightGenerator weight_generator_;
219 
220   DISALLOW_COPY_AND_ASSIGN(WeightTester);
221 };
222 
223 }  // namespace fst
224 
225 #endif  // FST_TEST_WEIGHT_TESTER_H_
226