| // weight-tester.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 |
| // Utility class for regression testing of Fst weights. |
| |
| #ifndef FST_TEST_WEIGHT_TESTER_H_ |
| #define FST_TEST_WEIGHT_TESTER_H_ |
| |
| #include <iostream> |
| #include <sstream> |
| |
| #include <fst/random-weight.h> |
| |
| namespace fst { |
| |
| // This class tests a variety of identities and properties that must |
| // hold for the Weight class to be well-defined. It calls function object |
| // WEIGHT_GENERATOR to select weights that are used in the tests. |
| template<class Weight, class WeightGenerator> |
| class WeightTester { |
| public: |
| WeightTester(WeightGenerator generator) : weight_generator_(generator) {} |
| |
| void Test(int iterations, bool test_division = true) { |
| for (int i = 0; i < iterations; ++i) { |
| // Selects the test weights. |
| Weight w1 = weight_generator_(); |
| Weight w2 = weight_generator_(); |
| Weight w3 = weight_generator_(); |
| |
| VLOG(1) << "weight type = " << Weight::Type(); |
| VLOG(1) << "w1 = " << w1; |
| VLOG(1) << "w2 = " << w2; |
| VLOG(1) << "w3 = " << w3; |
| |
| TestSemiring(w1, w2, w3); |
| if (test_division) |
| TestDivision(w1, w2); |
| TestReverse(w1, w2); |
| TestEquality(w1, w2, w3); |
| TestIO(w1); |
| TestCopy(w1); |
| } |
| } |
| |
| private: |
| // Note in the tests below we use ApproxEqual rather than == and add |
| // kDelta to inequalities where the weights might be inexact. |
| |
| // Tests (Plus, Times, Zero, One) defines a commutative semiring. |
| void TestSemiring(Weight w1, Weight w2, Weight w3) { |
| // Checks that the operations are closed. |
| CHECK(Plus(w1, w2).Member()); |
| CHECK(Times(w1, w2).Member()); |
| |
| // Checks that the operations are associative. |
| CHECK(ApproxEqual(Plus(w1, Plus(w2, w3)), Plus(Plus(w1, w2), w3))); |
| CHECK(ApproxEqual(Times(w1, Times(w2, w3)), Times(Times(w1, w2), w3))); |
| |
| // Checks the identity elements. |
| CHECK(Plus(w1, Weight::Zero()) == w1); |
| CHECK(Plus(Weight::Zero(), w1) == w1); |
| CHECK(Times(w1, Weight::One()) == w1); |
| CHECK(Times(Weight::One(), w1) == w1); |
| |
| // Check the no weight element. |
| CHECK(!Weight::NoWeight().Member()); |
| CHECK(!Plus(w1, Weight::NoWeight()).Member()); |
| CHECK(!Plus(Weight::NoWeight(), w1).Member()); |
| CHECK(!Times(w1, Weight::NoWeight()).Member()); |
| CHECK(!Times(Weight::NoWeight(), w1).Member()); |
| |
| // Checks that the operations commute. |
| CHECK(ApproxEqual(Plus(w1, w2), Plus(w2, w1))); |
| if (Weight::Properties() & kCommutative) |
| CHECK(ApproxEqual(Times(w1, w2), Times(w2, w1))); |
| |
| // Checks Zero() is the annihilator. |
| CHECK(Times(w1, Weight::Zero()) == Weight::Zero()); |
| CHECK(Times(Weight::Zero(), w1) == Weight::Zero()); |
| |
| // Check Power(w, 0) is Weight::One() |
| CHECK(Power(w1, 0) == Weight::One()); |
| |
| // Check Power(w, 1) is w |
| CHECK(Power(w1, 1) == w1); |
| |
| // Check Power(w, 3) is Times(w, Times(w, w)) |
| CHECK(Power(w1, 3) == Times(w1, Times(w1, w1))); |
| |
| // Checks distributivity. |
| if (Weight::Properties() & kLeftSemiring) |
| CHECK(ApproxEqual(Times(w1, Plus(w2, w3)), |
| Plus(Times(w1, w2), Times(w1, w3)))); |
| if (Weight::Properties() & kRightSemiring) |
| CHECK(ApproxEqual(Times(Plus(w1, w2), w3), |
| Plus(Times(w1, w3), Times(w2, w3)))); |
| |
| if (Weight::Properties() & kIdempotent) |
| CHECK(Plus(w1, w1) == w1); |
| |
| if (Weight::Properties() & kPath) |
| CHECK(Plus(w1, w2) == w1 || Plus(w1, w2) == w2); |
| |
| // Ensure weights form a left or right semiring. |
| CHECK(Weight::Properties() & (kLeftSemiring | kRightSemiring)); |
| |
| // Check when Times() is commutative that it is marked as a semiring. |
| if (Weight::Properties() & kCommutative) |
| CHECK(Weight::Properties() & kSemiring); |
| } |
| |
| // Tests division operation. |
| void TestDivision(Weight w1, Weight w2) { |
| Weight p = Times(w1, w2); |
| |
| if (Weight::Properties() & kLeftSemiring) { |
| Weight d = Divide(p, w1, DIVIDE_LEFT); |
| if (d.Member()) |
| CHECK(ApproxEqual(p, Times(w1, d))); |
| CHECK(!Divide(w1, Weight::NoWeight(), DIVIDE_LEFT).Member()); |
| CHECK(!Divide(Weight::NoWeight(), w1, DIVIDE_LEFT).Member()); |
| } |
| |
| if (Weight::Properties() & kRightSemiring) { |
| Weight d = Divide(p, w2, DIVIDE_RIGHT); |
| if (d.Member()) |
| CHECK(ApproxEqual(p, Times(d, w2))); |
| CHECK(!Divide(w1, Weight::NoWeight(), DIVIDE_RIGHT).Member()); |
| CHECK(!Divide(Weight::NoWeight(), w1, DIVIDE_RIGHT).Member()); |
| } |
| |
| if (Weight::Properties() & kCommutative) { |
| Weight d = Divide(p, w1, DIVIDE_RIGHT); |
| if (d.Member()) |
| CHECK(ApproxEqual(p, Times(d, w1))); |
| } |
| } |
| |
| // Tests reverse operation. |
| void TestReverse(Weight w1, Weight w2) { |
| typedef typename Weight::ReverseWeight ReverseWeight; |
| |
| ReverseWeight rw1 = w1.Reverse(); |
| ReverseWeight rw2 = w2.Reverse(); |
| |
| CHECK(rw1.Reverse() == w1); |
| CHECK(Plus(w1, w2).Reverse() == Plus(rw1, rw2)); |
| CHECK(Times(w1, w2).Reverse() == Times(rw2, rw1)); |
| } |
| |
| // Tests == is an equivalence relation. |
| void TestEquality(Weight w1, Weight w2, Weight w3) { |
| // Checks reflexivity. |
| CHECK(w1 == w1); |
| |
| // Checks symmetry. |
| CHECK((w1 == w2) == (w2 == w1)); |
| |
| // Checks transitivity. |
| if (w1 == w2 && w2 == w3) |
| CHECK(w1 == w3); |
| } |
| |
| // Tests binary serialization and textual I/O. |
| void TestIO(Weight w) { |
| // Tests binary I/O |
| { |
| ostringstream os; |
| w.Write(os); |
| os.flush(); |
| istringstream is(os.str()); |
| Weight v; |
| v.Read(is); |
| CHECK_EQ(w, v); |
| } |
| |
| // Tests textual I/O. |
| { |
| ostringstream os; |
| os << w; |
| istringstream is(os.str()); |
| Weight v(Weight::One()); |
| is >> v; |
| CHECK(ApproxEqual(w, v)); |
| } |
| } |
| |
| // Tests copy constructor and assignment operator |
| void TestCopy(Weight w) { |
| Weight x = w; |
| CHECK(w == x); |
| |
| x = Weight(w); |
| CHECK(w == x); |
| |
| x.operator=(x); |
| CHECK(w == x); |
| |
| } |
| |
| // Generates weights used in testing. |
| WeightGenerator weight_generator_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WeightTester); |
| }; |
| |
| } // namespace fst |
| |
| #endif // FST_TEST_WEIGHT_TESTER_H_ |