FST  openfst-1.8.3
OpenFst Library
weight-tester.h
Go to the documentation of this file.
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 // Utility class for regression testing of FST weights.
19 
20 #ifndef FST_TEST_WEIGHT_TESTER_H_
21 #define FST_TEST_WEIGHT_TESTER_H_
22 
23 #include <sstream>
24 #include <utility>
25 
26 #include <fst/log.h>
27 #include <fst/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 = WeightGenerate<Weight>>
35 class WeightTester {
36  public:
37  explicit WeightTester(WeightGenerator generator)
38  : weight_generator_(std::move(generator)) {}
39 
40  void Test(int iterations) {
41  for (int i = 0; i < iterations; ++i) {
42  // Selects the test weights.
43  const Weight w1(weight_generator_());
44  const Weight w2(weight_generator_());
45  const Weight w3(weight_generator_());
46 
47  VLOG(1) << "weight type = " << Weight::Type();
48  VLOG(1) << "w1 = " << w1;
49  VLOG(1) << "w2 = " << w2;
50  VLOG(1) << "w3 = " << w3;
51 
52  TestSemiring(w1, w2, w3);
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.
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 
91  if (Weight::Properties() & kCommutative)
92  CHECK(ApproxEqual(Times(w1, w2), Times(w2, w1)));
93 
94  // Checks Zero() is the annihilator.
95  CHECK(Times(w1, Weight::Zero()) == Weight::Zero());
96  CHECK(Times(Weight::Zero(), w1) == Weight::Zero());
97 
98  // Check Power(w, 0) is Weight::One()
99  CHECK(Power(w1, 0) == Weight::One());
100 
101  // Check Power(w, 1) is w
102  CHECK(Power(w1, 1) == w1);
103 
104  // Check Power(w, 2) is Times(w, w)
105  CHECK(Power(w1, 2) == Times(w1, w1));
106 
107  // Check Power(w, 3) is Times(w, Times(w, w))
108  CHECK(Power(w1, 3) == Times(w1, Times(w1, w1)));
109 
110  // Checks distributivity.
111  if (Weight::Properties() & kLeftSemiring) {
112  CHECK(ApproxEqual(Times(w1, Plus(w2, w3)),
113  Plus(Times(w1, w2), Times(w1, w3))));
114  }
115  if (Weight::Properties() & kRightSemiring)
116  CHECK(ApproxEqual(Times(Plus(w1, w2), w3),
117  Plus(Times(w1, w3), Times(w2, w3))));
118 
119  if (Weight::Properties() & kIdempotent) CHECK(Plus(w1, w1) == w1);
120 
121  if (Weight::Properties() & kPath)
122  CHECK(Plus(w1, w2) == w1 || Plus(w1, w2) == w2);
123 
124  // Ensure weights form a left or right semiring.
125  CHECK(Weight::Properties() & (kLeftSemiring | kRightSemiring));
126 
127  // Check when Times() is commutative that it is marked as a semiring.
128  if (Weight::Properties() & kCommutative)
129  CHECK(Weight::Properties() & kSemiring);
130  }
131 
132  // Tests division operation.
133  void TestDivision(Weight w1, Weight w2) {
134  Weight p = Times(w1, w2);
135  VLOG(1) << "TestDivision: p = " << p;
136 
137  if (Weight::Properties() & kLeftSemiring) {
138  Weight d = Divide(p, w1, DIVIDE_LEFT);
139  if (d.Member()) CHECK(ApproxEqual(p, Times(w1, d)));
140  CHECK(!Divide(w1, Weight::NoWeight(), DIVIDE_LEFT).Member());
141  CHECK(!Divide(Weight::NoWeight(), w1, DIVIDE_LEFT).Member());
142  }
143 
144  if (Weight::Properties() & kRightSemiring) {
145  Weight d = Divide(p, w2, DIVIDE_RIGHT);
146  if (d.Member()) CHECK(ApproxEqual(p, Times(d, w2)));
147  CHECK(!Divide(w1, Weight::NoWeight(), DIVIDE_RIGHT).Member());
148  CHECK(!Divide(Weight::NoWeight(), w1, DIVIDE_RIGHT).Member());
149  }
150 
151  if (Weight::Properties() & kCommutative) {
152  Weight d1 = Divide(p, w1, DIVIDE_ANY);
153  if (d1.Member()) CHECK(ApproxEqual(p, Times(d1, w1)));
154  Weight d2 = Divide(p, w2, DIVIDE_ANY);
155  if (d2.Member()) CHECK(ApproxEqual(p, Times(w2, d2)));
156  }
157  }
158 
159  // Tests reverse operation.
160  void TestReverse(Weight w1, Weight w2) {
161  using ReverseWeight = typename Weight::ReverseWeight;
162 
163  ReverseWeight rw1 = w1.Reverse();
164  ReverseWeight rw2 = w2.Reverse();
165 
166  CHECK(rw1.Reverse() == w1);
167  CHECK(Plus(w1, w2).Reverse() == Plus(rw1, rw2));
168  CHECK(Times(w1, w2).Reverse() == Times(rw2, rw1));
169  }
170 
171  // Tests == is an equivalence relation.
172  void TestEquality(Weight w1, Weight w2, Weight w3) {
173  // Checks reflexivity.
174  CHECK(w1 == w1);
175 
176  // Checks symmetry.
177  CHECK((w1 == w2) == (w2 == w1));
178 
179  // Checks transitivity.
180  if (w1 == w2 && w2 == w3) CHECK(w1 == w3);
181 
182  // Checks that two weights are either equal or not equal.
183  CHECK((w1 == w2) ^ (w1 != w2));
184 
185  if (w1 == w2) {
186  // Checks that equal weights have identical hashes.
187  CHECK(w1.Hash() == w2.Hash());
188  // Checks that equal weights are also approximately equal.
189  CHECK(ApproxEqual(w1, w2));
190  }
191 
192  // Checks that weights which are not even approximately equal are also
193  // strictly unequal.
194  if (!ApproxEqual(w1, w2)) {
195  CHECK(w1 != w2);
196  }
197  }
198 
199  // Tests binary serialization and textual I/O.
200  void TestIO(Weight w) {
201  // Tests binary I/O
202  {
203  std::ostringstream os;
204  w.Write(os);
205  os.flush();
206  std::istringstream is(os.str());
207  Weight v;
208  v.Read(is);
209  CHECK_EQ(w, v);
210  }
211 
212  // Tests textual I/O.
213  {
214  std::ostringstream os;
215  os << w;
216  std::istringstream is(os.str());
217  Weight v(Weight::One());
218  is >> v;
219  CHECK(ApproxEqual(w, v));
220  }
221  }
222 
223  // Tests copy constructor and assignment operator
224  void TestCopy(Weight w) {
225  Weight x = w;
226  CHECK(w == x);
227 
228  x = Weight(w);
229  CHECK(w == x);
230 
231  x.operator=(x);
232  CHECK(w == x);
233  }
234 
235  // Generates weights used in testing.
236  WeightGenerator weight_generator_;
237 };
238 
239 } // namespace fst
240 
241 #endif // FST_TEST_WEIGHT_TESTER_H_
constexpr uint64_t kSemiring
Definition: weight.h:141
ErrorWeight Plus(const ErrorWeight &, const ErrorWeight &)
Definition: error-weight.h:61
ErrorWeight Times(const ErrorWeight &, const ErrorWeight &)
Definition: error-weight.h:64
constexpr uint64_t kIdempotent
Definition: weight.h:147
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:36
constexpr uint64_t kRightSemiring
Definition: weight.h:139
WeightTester(WeightGenerator generator)
Definition: weight-tester.h:37
constexpr uint64_t kCommutative
Definition: weight.h:144
#define VLOG(level)
Definition: log.h:54
constexpr uint64_t kPath
Definition: weight.h:150
#define CHECK_EQ(x, y)
Definition: log.h:66
ErrorWeight Divide(const ErrorWeight &, const ErrorWeight &)
Definition: error-weight.h:67
void Test(int iterations)
Definition: weight-tester.h:40
#define CHECK(x)
Definition: log.h:65
constexpr TropicalWeightTpl< T > Power(const TropicalWeightTpl< T > &w, V n)
Definition: float-weight.h:401
constexpr uint64_t kLeftSemiring
Definition: weight.h:136
bool ApproxEqual(const ErrorWeight &, const ErrorWeight &, float)
Definition: error-weight.h:58