/*************************************************************************** * test_GNumerics.cpp - test numerics modules * * ----------------------------------------------------------------------- * * copyright (C) 2010-2018 by Jurgen Knodlseder * * ----------------------------------------------------------------------- * * * * This program 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. * * * * This program 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 this program. If not, see . * * * ***************************************************************************/ /** * @file test_GNumerics.cpp * @brief Unit tests implementation for numerics module * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "test_GNumerics.hpp" /* __ Namespaces _________________________________________________________ */ /* __ Globals ____________________________________________________________ */ /***********************************************************************//** * @brief Set tests ***************************************************************************/ void TestGNumerics::set(void) { //set name name("GNumerics"); // Set parameters m_sigma = 2.5; // Append tests append(static_cast(&TestGNumerics::test_math), "Test GMath"); append(static_cast(&TestGNumerics::test_ndarray), "Test GNdarray"); append(static_cast(&TestGNumerics::test_fft), "Test GFft"); append(static_cast(&TestGNumerics::test_integral), "Test GIntegral"); append(static_cast(&TestGNumerics::test_romberg_integration), "Test Romberg integration"); append(static_cast(&TestGNumerics::test_adaptive_simpson_integration), "Test adaptive Simpson integration"); append(static_cast(&TestGNumerics::test_gauss_kronrod_integration), "Test Gauss-Kronrod integration"); // Return return; } /***********************************************************************//** * @brief Clone test suite * * @return Pointer to deep copy of test suite. ***************************************************************************/ TestGNumerics* TestGNumerics::clone(void) const { // Clone test suite return new TestGNumerics(*this); } /***********************************************************************//** * @brief Test mathematics functions ***************************************************************************/ void TestGNumerics::test_math(void) { // Test acos function test_value(gammalib::acos(0.0), 1.57079633); test_value(gammalib::acos(0.37), 1.19178731); // Test atan2 function test_value(gammalib::atan2( 1.0, 2.0), 0.463647609001); test_value(gammalib::atan2( 1.0,-3.0), 2.819842099193); test_value(gammalib::atan2(-2.0,-3.0), -2.553590050042); test_value(gammalib::atan2(-2.0, 1.0), -1.107148717794); // Test cosd function test_value(gammalib::cosd( 0.0), 1.0); test_value(gammalib::cosd( 33.0), 0.83867057); test_value(gammalib::cosd(178.1), -0.99945022); // Test sind function test_value(gammalib::sind( 0.0), 0.0); test_value(gammalib::sind( 33.0), 0.54463904); test_value(gammalib::sind(178.1), 0.03315518); // Test tand function test_value(gammalib::tand(-10.0), -0.176326980708); test_value(gammalib::tand( 12.0), 0.21255656167); test_value(gammalib::tand(178.1), -0.0331734166041); // Test asind function test_value(gammalib::asind(0.0), 0.0); test_value(gammalib::asind(0.37), 21.7156172833); // Test acosd function test_value(gammalib::acosd(0.0), 90.0); test_value(gammalib::acosd(0.37), 68.28438272); // Test atan2d function test_value(gammalib::atan2d( 1.0, 2.0), 26.5650511771); test_value(gammalib::atan2d( 1.0,-3.0), 161.565051177); test_value(gammalib::atan2d(-2.0,-3.0), -146.309932474); test_value(gammalib::atan2d(-2.0, 1.0), -63.4349488229); // Test sincosd function double s; double c; gammalib::sincosd(33.0, &s, &c); test_value(s, 0.54463904); test_value(c, 0.83867057); // Test logarithm of gamma function test_value(gammalib::gammln(0.5), 0.572364942925); test_value(gammalib::gammln(1.5), -0.120782237635); // Test error function test_value(gammalib::erf(0.0), 0.0); test_value(gammalib::erf(1.0), 0.8427008); test_value(gammalib::erf(2.0), 0.9953223); // Test complementary error function test_value(gammalib::erfc(0.0), 1.0); test_value(gammalib::erfc(0.1), 0.887537084); test_value(gammalib::erfc(0.8), 0.257899035); // Test inverse error function test_value(gammalib::erfinv(0.3), 0.2724627147); test_value(gammalib::erfinv(0.9), 1.1630871537); // Test modulo function test_value(gammalib::modulo(3.1, 2.8), 0.3); test_value(gammalib::modulo(-5.7, 1.37), 1.15); // Test power law integration test_value(gammalib::plaw_integral(2.0, 3.0, 9.0, 5.0), 29.112577); test_value(gammalib::plaw_integral(2.0, 9.0, 9.0, 2.0), 27.073393); test_value(gammalib::plaw_integral(2.0, 9.0, 5.0, 2.0), 12.471065); // Test Gaussian integration test_value(gammalib::gauss_integral(0.0, 1.0), 0.341344746069); test_value(gammalib::gauss_integral(-1.0, 1.0), 0.682689492137086); test_value(gammalib::gauss_integral(-2.0, 2.0), 0.954499736103642); test_value(gammalib::gauss_integral(-3.0, 3.0), 0.997300203936740); // Exit test return; } /***********************************************************************//** * @brief Test n-dimensional array ***************************************************************************/ void TestGNumerics::test_ndarray(void) { // Test void constructor GNdarray nd0; test_value(nd0.size(), 0); test_value(nd0.dim(), 0); // Test 1-dimensional array GNdarray nd1(3); test_value(nd1.size(), 3); test_value(nd1.dim(), 1); test_value(nd1.shape()[0], 3); for (int i = 0; i < 3; ++i) { nd1(i) = double(i); test_value(nd1.at(i), double(i)); } // Test 2-dimensional array GNdarray nd2(3,2); test_value(nd2.size(), 6); test_value(nd2.dim(), 2); test_value(nd2.shape()[0], 3); test_value(nd2.shape()[1], 2); for (int ix = 0; ix < 3; ++ix) { for (int iy = 0; iy < 2; ++iy) { double ref = double(ix)+double(iy); nd2(ix,iy) = ref; test_value(nd2.at(ix,iy), ref); } } // Test 3-dimensional array GNdarray nd3(2,2,2); test_value(nd3.size(), 8); test_value(nd3.dim(), 3); test_value(nd3.shape()[0], 2); test_value(nd3.shape()[1], 2); test_value(nd3.shape()[2], 2); for (int ix = 0; ix < 2; ++ix) { for (int iy = 0; iy < 2; ++iy) { for (int iz = 0; iz < 2; ++iz) { double ref = double(ix)+double(iy)+double(iz); nd3(ix,iy,iz) = ref; test_value(nd3.at(ix,iy,iz), ref); } } } // Test n-dimensional array std::vector n(5,2); GNdarray ndn(n); test_value(ndn.size(), 32); test_value(ndn.dim(), 5); for (int i = 0; i < 5; ++i) { test_value(ndn.shape()[i], 2); } std::vector itest(5,1); ndn(itest) = 3.1415; test_value(ndn.at(itest), 3.1415); // Setup arrays for operator tests GNdarray nda(1,2); nda(0,0) = 3.0; nda(0,1) = 5.5; GNdarray ndb = nda; GNdarray ndc = nda + ndb; // Test operator= test_value(ndb(0,0), 3.0); test_value(ndb(0,1), 5.5); // Test operator== and operator!= test_assert(nda == ndb, "Test operator=="); test_assert(nda != ndc, "Test operator!="); // Test array operator+= nda += ndb; test_value(nda(0,0), 6.0); test_value(nda(0,1), 11.0); // Test array operator-= nda -= ndb; test_value(nda(0,0), 3.0); test_value(nda(0,1), 5.5); // Test value operator+= nda += 1.5; test_value(nda(0,0), 4.5); test_value(nda(0,1), 7.0); // Test value operator-= nda -= 1.5; test_value(nda(0,0), 3.0); test_value(nda(0,1), 5.5); // Test value operator*= nda *= 3.0; test_value(nda(0,0), 9.0); test_value(nda(0,1), 16.5); // Test value operator/= nda /= 3.0; test_value(nda(0,0), 3.0); test_value(nda(0,1), 5.5); // Test operator- GNdarray ndd = -nda; test_value(ndd(0,0), -3.0); test_value(ndd(0,1), -5.5); // Test operator+(GNdarray,GNdarray) ndd = nda + ndb; test_value(ndd(0,0), 6.0); test_value(ndd(0,1), 11.0); // Test operator+(GNdarray,double) and operator+(double,GNdarray) ndd = nda + 1.5; test_value(ndd(0,0), 4.5); test_value(ndd(0,1), 7.0); ndd = 1.5 + nda; test_value(ndd(0,0), 4.5); test_value(ndd(0,1), 7.0); // Test operator-(GNdarray,GNdarray) ndd = nda - ndb; test_value(ndd(0,0), 0.0); test_value(ndd(0,1), 0.0); // Test operator-(GNdarray,double) and operator-(double,GNdarray) ndd = nda - 1.5; test_value(ndd(0,0), 1.5); test_value(ndd(0,1), 4.0); ndd = 1.5 - nda; test_value(ndd(0,0), -1.5); test_value(ndd(0,1), -4.0); // Test operator*(GNdarray,double) and operator*(double,GNdarray) ndd = nda * 3.0; test_value(ndd(0,0), 9.0); test_value(ndd(0,1), 16.5); ndd = 3.0 * nda; test_value(ndd(0,0), 9.0); test_value(ndd(0,1), 16.5); // Test operator/(GNdarray,double) ndd = nda / 3.0; test_value(ndd(0,0), 1.0); test_value(ndd(0,1), 5.5/3.0); // Test friend functions test_value(min(nda), 3.0); test_value(max(nda), 5.5); test_value(sum(nda), 8.5); test_value(acos(nda)(0,0), std::acos(3.0)); test_value(acos(nda)(0,1), std::acos(5.5)); test_value(acosh(nda)(0,0), acosh(3.0)); test_value(acosh(nda)(0,1), acosh(5.5)); test_value(asin(nda)(0,0), std::asin(3.0)); test_value(asin(nda)(0,1), std::asin(5.5)); test_value(asinh(nda)(0,0), asinh(3.0)); test_value(asinh(nda)(0,1), asinh(5.5)); test_value(atan(nda)(0,0), std::atan(3.0)); test_value(atan(nda)(0,1), std::atan(5.5)); test_value(atanh(nda)(0,0), atanh(3.0)); test_value(atanh(nda)(0,1), atanh(5.5)); test_value(cos(nda)(0,0), std::cos(3.0)); test_value(cos(nda)(0,1), std::cos(5.5)); test_value(cosh(nda)(0,0), std::cosh(3.0)); test_value(cosh(nda)(0,1), std::cosh(5.5)); test_value(exp(nda)(0,0), std::exp(3.0)); test_value(exp(nda)(0,1), std::exp(5.5)); test_value(abs(nda)(0,0), std::abs(3.0)); test_value(abs(nda)(0,1), std::abs(5.5)); test_value(log(nda)(0,0), std::log(3.0)); test_value(log(nda)(0,1), std::log(5.5)); test_value(log10(nda)(0,0), std::log10(3.0)); test_value(log10(nda)(0,1), std::log10(5.5)); test_value(sin(nda)(0,0), std::sin(3.0)); test_value(sin(nda)(0,1), std::sin(5.5)); test_value(sinh(nda)(0,0), std::sinh(3.0)); test_value(sinh(nda)(0,1), std::sinh(5.5)); test_value(sqrt(nda)(0,0), std::sqrt(3.0)); test_value(sqrt(nda)(0,1), std::sqrt(5.5)); test_value(tan(nda)(0,0), std::tan(3.0)); test_value(tan(nda)(0,1), std::tan(5.5)); test_value(tanh(nda)(0,0), std::tanh(3.0)); test_value(tanh(nda)(0,1), std::tanh(5.5)); test_value(pow(nda,3.0)(0,0), std::pow(3.0,3.0)); test_value(pow(nda,3.0)(0,1), std::pow(5.5,3.0)); // Exit test return; } /***********************************************************************//** * @brief Test Fast Fourier Transform ***************************************************************************/ void TestGNumerics::test_fft(void) { // Test 1-dimensional FFT for (int n = 2; n < 12; ++n) { // Skip n=8,9,10 since they can be factorised if (n > 7 && n < 11) { continue; } // Allocate 1-dimensional array GNdarray array(n); // Set one elements array(n/2) = 1.0; // Allocate FFT GFft fft(array); // Backward transformation GNdarray back = fft.backward(); // Check FFT results check_fft1(array, fft, back); } // endfor: looped over array lengths // Test 2-dimensional FFT GNdarray array2(8,9); array2(4,4) = 1.0; GFft fft2(array2); GNdarray back2 = fft2.backward(); check_fft2(array2, fft2, back2); // Exit test return; } /***********************************************************************//** * @brief Check 1-dimensional Fast Fourier Transform * * @param[in] array Input array. * @param[in] fft Fast Fourier Transform of input array. * @param[in] back Backward transform of FFT (should be equal to input array) ***************************************************************************/ void TestGNumerics::check_fft1(const GNdarray& array, const GFft& fft, const GNdarray& back) { // Get normalisation factor double norm = 1.0 / double(array.size()); // Compute reference value and compare with FFT result for (int k = 0; k < array.size(); ++k) { double ref_real = 0.0; double ref_imag = 0.0; for (int n = 0; n < array.size(); ++n) { double x = -gammalib::twopi * k * n * norm; ref_real += array(n) * std::cos(x); ref_imag += array(n) * std::sin(x); } std::complex ref(ref_real, ref_imag); test_value(fft(k), ref); } // Test backward transformation for (int k = 0; k < array.size(); ++k) { test_value(back(k), array(k)); } // Exit test return; } /***********************************************************************//** * @brief Check 2-dimensional Fast Fourier Transform * * @param[in] array Input array. * @param[in] fft Fast Fourier Transform of input array. * @param[in] back Backward transform of FFT (should be equal to input array) ***************************************************************************/ void TestGNumerics::check_fft2(const GNdarray& array, const GFft& fft, const GNdarray& back) { // Get normalisation factor double normM = 1.0 / double(array.shape()[0]); double normN = 1.0 / double(array.shape()[1]); // Compute reference value and compare with FFT result for (int k = 0; k < array.shape()[0]; ++k) { for (int l = 0; l < array.shape()[1]; ++l) { double ref_real = 0.0; double ref_imag = 0.0; for (int n = 0; n < array.shape()[1]; ++n) { double sum_real = 0.0; double sum_imag = 0.0; for (int m = 0; m < array.shape()[0]; ++m) { double x = -gammalib::twopi * m * k * normM; sum_real += array(m,n) * std::cos(x); sum_imag += array(m,n) * std::sin(x); } double y = -gammalib::twopi * n * l * normN; ref_real += (sum_real * std::cos(y) - sum_imag * std::sin(y)); ref_imag += (sum_real * std::sin(y) + sum_imag * std::cos(y)); } std::complex ref(ref_real, ref_imag); test_value(fft(k,l), ref); } } // Test backward transformation for (int k = 0; k < array.size(); ++k) { test_value(back(k), array(k)); } // Exit test return; } /***********************************************************************//** * @brief Test model parameter handling ***************************************************************************/ void TestGNumerics::test_integral(void) { // Test integral and integrand allocation Gauss integrand(m_sigma); GIntegral integral(&integrand); // Exit test return; } /***********************************************************************//** * @brief Test Romberg integration ***************************************************************************/ void TestGNumerics::test_romberg_integration(void) { // Set-up integral Gauss integrand(m_sigma); GIntegral integral(&integrand); // Integrate over the entire Gaussian double result = integral.romberg(-10.0*m_sigma, 10.0*m_sigma); test_value(result,1.0,1.0e-6,"","Gaussian integral is not 1.0 (integral="+gammalib::str(result)+")"); // Test [-1sigma, 1sigma] result = integral.romberg(-m_sigma, m_sigma); test_value(result,0.68268948130801355,1.0e-6,"","Gaussian integral is not 0.682689 (difference="+gammalib::str((result-0.68268948130801355))+")"); // Test [0.0, 1sigma] result = integral.romberg(0.0, m_sigma); test_value(result,0.3413447460687748,1.0e-6,"","Gaussian integral is not 0.341345 (difference="+gammalib::str((result-0.3413447460687748))+")"); // Return return; } /***********************************************************************//** * @brief Test adaptive Simpson integration ***************************************************************************/ void TestGNumerics::test_adaptive_simpson_integration(void) { // Set-up integral Gauss integrand(m_sigma); GIntegral integral(&integrand); // Integrate over the entire Gaussian double result = integral.adaptive_simpson(-10.0*m_sigma, 10.0*m_sigma); /* std::cout << result << std::endl; std::cout << result-1.0 << std::endl; std::cout << integral << std::endl; */ test_value(result,1.0,1.0e-6,"","Gaussian integral is not 1.0 (integral="+gammalib::str(result)+")"); // Test [-1sigma, 1sigma] result = integral.adaptive_simpson(-m_sigma, m_sigma); /* std::cout << result << std::endl; std::cout << result-0.68268948130801355 << std::endl; std::cout << integral << std::endl; */ test_value(result,0.68268948130801355,1.0e-6,"","Gaussian integral is not 0.682689 (difference="+gammalib::str((result-0.68268948130801355))+")"); // Test [0.0, 1sigma] result = integral.adaptive_simpson(0.0, m_sigma); /* std::cout << result << std::endl; std::cout << result-0.3413447460687748 << std::endl; std::cout << integral << std::endl; */ test_value(result,0.3413447460687748,1.0e-6,"","Gaussian integral is not 0.341345 (difference="+gammalib::str((result-0.3413447460687748))+")"); // Return return; } /***********************************************************************//** * @brief Test Gauss-Kronrod integration ***************************************************************************/ void TestGNumerics::test_gauss_kronrod_integration(void) { // Set-up integral Gauss integrand(m_sigma); GIntegral integral(&integrand); // Integrate over the entire Gaussian double result = integral.gauss_kronrod(-10.0*m_sigma, 10.0*m_sigma); /* std::cout << result << std::endl; std::cout << result-1.0 << std::endl; std::cout << integral << std::endl; */ test_value(result,1.0,1.0e-6,"","Gaussian integral is not 1.0 (integral="+gammalib::str(result)+")"); // Test [-1sigma, 1sigma] result = integral.gauss_kronrod(-m_sigma, m_sigma); /* std::cout << result << std::endl; std::cout << result-0.68268948130801355 << std::endl; std::cout << integral << std::endl; */ test_value(result,0.68268948130801355,1.0e-6,"","Gaussian integral is not 0.682689 (difference="+gammalib::str((result-0.68268948130801355))+")"); // Test [0.0, 1sigma] result = integral.gauss_kronrod(0.0, m_sigma); /* std::cout << result << std::endl; std::cout << result-0.3413447460687748 << std::endl; std::cout << integral << std::endl; */ test_value(result,0.3413447460687748,1.0e-6,"","Gaussian integral is not 0.341345 (difference="+gammalib::str((result-0.3413447460687748))+")"); // Return return; } /***********************************************************************//** * @brief Main test function ***************************************************************************/ int main(void) { GTestSuites testsuite("GNumerics"); bool was_successful=true; //Create a test suite TestGNumerics test; //Append to the container testsuite.append(test); //Run was_successful=testsuite.run(); //save xml report testsuite.save("reports/GNumerics.xml"); // Return return was_successful ? 0:1; }