/* This file is part of MAUS: http://micewww.pp.rl.ac.uk/projects/maus * * MAUS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MAUS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MAUS. If not, see . * */ #include #include #include "gtest/gtest.h" #include "src/legacy/Interface/MathUtils.hh" #include "src/legacy/Interface/STLUtils.hh" namespace { // Used to remove duplicated values in e.g. lists of polynomials (see docs) TEST(MathUtils, CompactVectorTest) { std::vector< std::vector > vec; vec.push_back(std::vector(1, 0)); vec.push_back(std::vector(4, 1)); vec[1][0] = -2; vec[1][2] = 0; vec.push_back(std::vector(4, 2)); vec.push_back(std::vector(4, 1)); vec.push_back(std::vector(4, 1)); vec[2][2] = 0; vec[3][2] = 0; vec.push_back(std::vector(4, 1)); vec[4][2] = 0; std::vector< std::vector > out = MathUtils::CompactVector(vec); std::vector< std::vector > all_tests; all_tests.push_back(std::vector(1, 0)); all_tests.push_back(std::vector(4, 2)); all_tests.push_back(std::vector(4, 1)); all_tests.push_back(std::vector(4, 1)); all_tests[1][2] = 0; all_tests[2][0] = 0; all_tests[2][2] = 0; // expect vector to contain test1, test2 and test3 in some order ASSERT_EQ(out.size(), all_tests.size()); for (size_t i = 0; i < out.size(); i++) for (size_t j = 0; j < all_tests.size(); j++) if (STLUtils::IterableEquality(all_tests[j], out[i])) all_tests.erase(all_tests.begin()+j); ASSERT_EQ(all_tests.size(), size_t(0)); } // Helper function (comparator) to do sort before we compact TEST(Mathutils, GreaterThanTest) { std::vector v1(3, 1); std::vector v2(3, 1); std::vector v3(2, 1); EXPECT_FALSE(MathUtils::GreaterThan(v1, v2)) << "v1 == v2 should return false"; v1[1] = 2; v2[0] = 2; EXPECT_TRUE(MathUtils::GreaterThan(v1, v2)); EXPECT_FALSE(MathUtils::GreaterThan(v2, v1)); v1[1] = 1; EXPECT_FALSE(MathUtils::GreaterThan(v1, v3)); v3[1] = 0; EXPECT_TRUE(MathUtils::GreaterThan(v1, v3)); } class EngeTest : public ::testing::Test { protected: EngeTest() { _lambda = 0.5; double _enge_a[4] = {0., 1., 0., 0.}; _a = std::vector(_enge_a, _enge_a+4); _x0 = 3.; _enge = Enge(_a, _x0, _lambda, 10); } virtual ~EngeTest() {} virtual void SetUp() {} virtual void TearDown() {} Enge _e_zero, _enge; std::vector _a; double _lambda, _x0; }; // compare numerical derivative with analytical derivative // check that f' < tolerance in the range xmin -> xmax, for all derivatives up to // max_n (where n=1 is the first derivative). (n+1)th numerical derivative is // calculated from nth analytical derivative using standard formula // f' = ( f(x+dx)-f(x-dx) )/2dx // Deal with two cases: // * where f is large, requiring large tolerance on numerical derivative // * where f is small, requiring fixed tolerance on numerical derivative // we use the largest of y*dy_tol and dy_tol as the test tolerance on difference // between numerical and analytical derivative template void test_deriv(T object, double (T::*function)(double, int) const, double dx, double xmin, double xmax, double dy_tol, int max_n) { for (int n = 1; n <= max_n; ++n) { for (double x = xmin; x < xmax; x += (xmax-xmin)/100.) { double y = (object.*function)(x, n); if (y*dy_tol > dy_tol) dy_tol = y*dy_tol; ASSERT_NEAR(y, ((object.*function)(x+dx, n-1) -(object.*function)(x-dx, n-1))/2./dx, dy_tol) << n << " " << x; } } } // check the constructor is okay - just test that it sets parameters correctly TEST_F(EngeTest, EngeConstructorTest) { std::vector b, c; b = _e_zero.GetEngeParameters(); EXPECT_TRUE(STLUtils::IterableEquality(b, c) ); EXPECT_EQ(_e_zero.GetLambda(), 0); Enge enge_alt_zero(c, 0., 0., 10); b = enge_alt_zero.GetEngeParameters(); EXPECT_TRUE(STLUtils::IterableEquality(b, c) ); EXPECT_EQ(enge_alt_zero.GetLambda(), 0.); EXPECT_EQ(enge_alt_zero.GetX0(), 0.); b = _enge.GetEngeParameters(); EXPECT_TRUE(STLUtils::IterableEquality(b, _a) ); EXPECT_EQ(_enge.GetLambda(), _lambda); EXPECT_EQ(_enge.GetX0(), _x0); } // check Set functions actually set stuff TEST_F(EngeTest, MutatorAccessorTest) { std::vector b; _enge.SetEngeParameters(b); EXPECT_TRUE(STLUtils::IterableEquality(b, _enge.GetEngeParameters())); _enge.SetEngeParameters(_a); EXPECT_TRUE(STLUtils::IterableEquality(_a, _enge.GetEngeParameters())); _enge.SetLambda(0.); EXPECT_EQ(_enge.GetLambda(), 0.); _enge.SetLambda(_lambda); EXPECT_EQ(_enge.GetLambda(), _lambda); _enge.SetX0(0.); EXPECT_EQ(_enge.GetX0(), 0.); _enge.SetX0(_x0); EXPECT_EQ(_enge.GetX0(), _x0); } // check that we set diff indices correctly - compare indices calculated by hand // with indices calculated by code // nb: diff indices index e.g. powers of d^n/dx^n (1+e^f) in enge(n) TEST_F(EngeTest, SetEngeDiffIndicesTest) { int qt[] = { // test q -6, -4, 3, /**/ 4, -3, 1, 1, /**/ 2, -3, 1, 1, /**/ -1, -2, 0, 0, 1}; std::vector< std::vector > q = Enge::GetQIndex(3); ASSERT_EQ(q.size(), size_t(4)); ASSERT_EQ(q[0].size(), size_t(3)); for (size_t i = 0; i < 3; ++i) EXPECT_EQ(qt[0+i], q[0][i]); ASSERT_EQ(q[1].size(), size_t(4)); for (size_t i = 0; i < 4; ++i) EXPECT_EQ(qt[3+i], q[1][i]); ASSERT_EQ(q[2].size(), size_t(4)); for (size_t i = 0; i < 4; ++i) EXPECT_EQ(qt[7+i], q[2][i]); ASSERT_EQ(q[3].size(), size_t(5)); for (size_t i = 0; i < 5; ++i) EXPECT_EQ(qt[11+i], q[3][i]); std::vector< std::vector > h = Enge::GetHIndex(4); int ht[] = { // test h 1, 4, /**/ 6, 2, 1, /**/ 4, 1, 0, 1, /**/ 3, 0, 2, /**/ 1, 0, 0, 0, 1}; std::vector< std::vector > htv(5); htv[0] = std::vector(&ht[0], &ht[2]); htv[1] = std::vector(&ht[2], &ht[5]); htv[2] = std::vector(&ht[5], &ht[9]); htv[3] = std::vector(&ht[9], &ht[12]); htv[4] = std::vector(&ht[12], &ht[17]); ASSERT_EQ(h.size(), htv.size()); for (size_t i = 0; i < h.size(); ++i) for (size_t j = 0; j < htv.size(); ++j) if (STLUtils::IterableEquality(htv[j], h[i])) htv.erase(htv.begin()+j); ASSERT_EQ(htv.size(), size_t(0)); } // check that we calculate d^n/dx^n h correctly - calculate explicitly h for a // values and then compare numerical and analytical derivatives TEST_F(EngeTest, HNTest) { for (double x = -10.; x < 11; x++) { double y = 0.; for (size_t i = 0; i < _a.size(); i++) y += _a[i]*pow(x/_lambda, i); EXPECT_NEAR(_enge.HN(x, 0), y, 1e-6); } test_deriv(_enge, &Enge::HN, 1e-3, -10., 10., 1e-6, 9); } // check that we calculate d^n/dx^n g correctly - calculate explicitly g for a // few x values and then compare numerical and analytical derivatives TEST_F(EngeTest, GNTest) { for (double x = -10; x < 11; x++) { EXPECT_DOUBLE_EQ(_enge.GN(x, 0), 1+exp(_enge.HN(x, 0))); } test_deriv(_enge, &Enge::GN, 1e-6, -10., 10., 1e-6, 9); } // check that we calculate d^n/dx^n enge correctly - calculate explicitly g for // a few x values and then compare numerical and analytical derivatives TEST_F(EngeTest, GetEngeTest) { for (double x = -10; x < 11; x++) EXPECT_DOUBLE_EQ(_enge.GetEnge(x, 0), 1./(_enge.GN(x, 0))); test_deriv(_enge, &Enge::GetEnge, 1e-9, -10., 10., 1e-6, 9); } // double enge gives us a double ended enge function - test function value and // derivatives TEST_F(EngeTest, GetDoubleEngeTest) { // difference at order 1e-15 for (double x = -10; x < 11; x++) EXPECT_NEAR(_enge.GetDoubleEnge(-x, 0), (_enge.GetEnge(x-_x0, 0)+_enge.GetEnge(-x-_x0, 0))-1., 1e-12 ) << x; test_deriv(_enge, &Enge::GetDoubleEnge, 1e-6, -10., 10., 1e-3, 9); } ////////// TANH ////////////// class TanhTest : public ::testing::Test { protected: TanhTest() :_x0(11.2439), _lambda(2.27567) { _d_tanh = Tanh(_x0, _lambda, 10); } virtual ~TanhTest() {} virtual void SetUp() {} virtual void TearDown() {} Tanh _d_tanh, _d_tanh_zero; double _x0, _lambda; }; // check that values are set correctly TEST_F(TanhTest, TanhConstructorTest) { EXPECT_EQ(_d_tanh_zero.GetLambda(), 0.); EXPECT_EQ(_d_tanh_zero.GetX0(), 0); EXPECT_EQ(_d_tanh.GetLambda(), _lambda); EXPECT_EQ(_d_tanh.GetX0(), _x0); } // check that Set functions set correctly TEST_F(TanhTest, TanhMutatorAccessorTest) { _d_tanh_zero.SetLambda(_lambda); _d_tanh_zero.SetX0(_x0); EXPECT_EQ(_d_tanh_zero.GetLambda(), _lambda); EXPECT_EQ(_d_tanh_zero.GetX0(), _x0); } // check that we calculate the diff indices okay (compare with some indices // calculated by hand) TEST_F(TanhTest, TanhGetTanhDiffIndicesTest) { Tanh::SetTanhDiffIndices(1); int tdi1[6] = {-6, 4, /**/ 8, 2, /**/ -2, 0}; std::vector< std::vector > tdi2 = Tanh::GetTanhDiffIndices(3); ASSERT_EQ(size_t(3), tdi2.size()); for (size_t i = 0; i < tdi2.size(); ++i) { for (size_t j = 0; j < 2; ++j) ASSERT_EQ(tdi2[i][j], tdi1[i*2+j]); } } // tanh + derivatives TEST_F(TanhTest, GetTanhTest) { for (double x = -10.; x < 11; x++) ASSERT_NEAR(_d_tanh.GetTanh(x, 0), tanh((x+_x0)/_lambda), 1e-9); test_deriv(_d_tanh, &Tanh::GetTanh, 1e-6, -10., 10., 1e-6, 6); } // neg tanh + derivatives (tanh(-x+x0)) TEST_F(TanhTest, GetNegTanhTest) { for (double x = -10.; x < 11; x++) ASSERT_NEAR(_d_tanh.GetNegTanh(x, 0), tanh((x-_x0)/_lambda), 1e-9); test_deriv(_d_tanh, &Tanh::GetNegTanh, 1e-6, -10., 10., 1e-6, 6); } // sum of tanh and neg tanh TEST_F(TanhTest, GetDoubleTanhTest) { for (double x = -20.; x < 21; x++) ASSERT_NEAR(2.*_d_tanh.GetDoubleTanh(x, 0), tanh((x+_x0)/_lambda)-tanh((x-_x0)/_lambda), 1e-12); test_deriv(_d_tanh, &Tanh::GetTanh, 1e-6, -100., 100., 1e-6, 6); } }