// Copyright Nick Thompson, 2017 // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #define BOOST_TEST_MODULE Gauss Kronrod_quadrature_test #include #include #include #if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES) && !defined(BOOST_NO_SFINAE_EXPR) #include #include #include #include #include #include #include #include #ifdef BOOST_HAS_FLOAT128 #include #endif #if !defined(TEST1) && !defined(TEST1A) && !defined(TEST2) && !defined(TEST3) # define TEST1 # define TEST1A # define TEST2 # define TEST3 #endif #ifdef _MSC_VER #pragma warning(disable:4127) // Conditional expression is constant #endif using std::expm1; using std::atan; using std::tan; using std::log; using std::log1p; using std::asinh; using std::atanh; using std::sqrt; using std::isnormal; using std::abs; using std::sinh; using std::tanh; using std::cosh; using std::pow; using std::exp; using std::sin; using std::cos; using std::string; using boost::math::quadrature::gauss_kronrod; using boost::math::constants::pi; using boost::math::constants::half_pi; using boost::math::constants::two_div_pi; using boost::math::constants::two_pi; using boost::math::constants::half; using boost::math::constants::third; using boost::math::constants::half; using boost::math::constants::third; using boost::math::constants::catalan; using boost::math::constants::ln_two; using boost::math::constants::root_two; using boost::math::constants::root_two_pi; using boost::math::constants::root_pi; using boost::multiprecision::cpp_bin_float_quad; using boost::multiprecision::cpp_dec_float_50; using boost::multiprecision::debug_adaptor; using boost::multiprecision::number; // // Error rates depend only on the number of points in the approximation, not the type being tested, // define all our expected errors here: // enum { test_ca_error_id, test_ca_error_id_2, test_three_quad_error_id, test_three_quad_error_id_2, test_integration_over_real_line_error_id, test_right_limit_infinite_error_id, test_left_limit_infinite_error_id }; template double expected_error(unsigned) { return 0; // placeholder, all tests will fail } template <> double expected_error<15>(unsigned id) { switch (id) { case test_ca_error_id: return 1e-7; case test_ca_error_id_2: return 2e-5; case test_three_quad_error_id: return 1e-8; case test_three_quad_error_id_2: return 3.5e-3; case test_integration_over_real_line_error_id: return 6e-3; case test_right_limit_infinite_error_id: case test_left_limit_infinite_error_id: return 1e-5; } return 0; // placeholder, all tests will fail } template <> double expected_error<17>(unsigned id) { switch (id) { case test_ca_error_id: return 1e-7; case test_ca_error_id_2: return 2e-5; case test_three_quad_error_id: return 1e-8; case test_three_quad_error_id_2: return 3.5e-3; case test_integration_over_real_line_error_id: return 6e-3; case test_right_limit_infinite_error_id: case test_left_limit_infinite_error_id: return 1e-5; } return 0; // placeholder, all tests will fail } template <> double expected_error<21>(unsigned id) { switch (id) { case test_ca_error_id: return 1e-12; case test_ca_error_id_2: return 3e-6; case test_three_quad_error_id: return 2e-13; case test_three_quad_error_id_2: return 2e-3; case test_integration_over_real_line_error_id: return 6e-3; // doesn't get any better with more points! case test_right_limit_infinite_error_id: case test_left_limit_infinite_error_id: return 5e-8; } return 0; // placeholder, all tests will fail } template <> double expected_error<31>(unsigned id) { switch (id) { case test_ca_error_id: return 6e-20; case test_ca_error_id_2: return 3e-7; case test_three_quad_error_id: return 1e-19; case test_three_quad_error_id_2: return 6e-4; case test_integration_over_real_line_error_id: return 6e-3; // doesn't get any better with more points! case test_right_limit_infinite_error_id: case test_left_limit_infinite_error_id: return 5e-11; } return 0; // placeholder, all tests will fail } template <> double expected_error<41>(unsigned id) { switch (id) { case test_ca_error_id: return 1e-26; case test_ca_error_id_2: return 1e-7; case test_three_quad_error_id: return 3e-27; case test_three_quad_error_id_2: return 3e-4; case test_integration_over_real_line_error_id: return 5e-5; // doesn't get any better with more points! case test_right_limit_infinite_error_id: case test_left_limit_infinite_error_id: return 1e-15; } return 0; // placeholder, all tests will fail } template <> double expected_error<51>(unsigned id) { switch (id) { case test_ca_error_id: return 5e-33; case test_ca_error_id_2: return 1e-8; case test_three_quad_error_id: return 1e-32; case test_three_quad_error_id_2: return 3e-4; case test_integration_over_real_line_error_id: return 1e-14; case test_right_limit_infinite_error_id: case test_left_limit_infinite_error_id: return 3e-19; } return 0; // placeholder, all tests will fail } template <> double expected_error<61>(unsigned id) { switch (id) { case test_ca_error_id: return 5e-34; case test_ca_error_id_2: return 5e-9; case test_three_quad_error_id: return 4e-34; case test_three_quad_error_id_2: return 1e-4; case test_integration_over_real_line_error_id: return 1e-16; case test_right_limit_infinite_error_id: case test_left_limit_infinite_error_id: return 3e-23; } return 0; // placeholder, all tests will fail } template void test_linear() { std::cout << "Testing linear functions are integrated properly by gauss_kronrod on type " << boost::typeindex::type_id().pretty_name() << "\n"; Real tol = boost::math::tools::epsilon() * 10; Real error; auto f = [](const Real& x)->Real { return 5*x + 7; }; Real L1; Real Q = gauss_kronrod::integrate(f, (Real) 0, (Real) 1, 0, 0, &error, &L1); BOOST_CHECK_CLOSE_FRACTION(Q, 9.5, tol); BOOST_CHECK_CLOSE_FRACTION(L1, 9.5, tol); } template void test_quadratic() { std::cout << "Testing quadratic functions are integrated properly by Gauss Kronrod on type " << boost::typeindex::type_id().pretty_name() << "\n"; Real tol = boost::math::tools::epsilon() * 10; Real error; auto f = [](const Real& x)->Real { return 5*x*x + 7*x + 12; }; Real L1; Real Q = gauss_kronrod::integrate(f, 0, 1, 0, 0, &error, &L1); BOOST_CHECK_CLOSE_FRACTION(Q, (Real) 17 + half()*third(), tol); BOOST_CHECK_CLOSE_FRACTION(L1, (Real) 17 + half()*third(), tol); } // Examples taken from //http://crd-legacy.lbl.gov/~dhbailey/dhbpapers/quadrature.pdf template void test_ca() { std::cout << "Testing integration of C(a) on type " << boost::typeindex::type_id().pretty_name() << "\n"; Real tol = expected_error(test_ca_error_id); Real L1; Real error; auto f1 = [](const Real& x)->Real { return atan(x)/(x*(x*x + 1)) ; }; Real Q = gauss_kronrod::integrate(f1, 0, 1, 0, 0, &error, &L1); Real Q_expected = pi()*ln_two()/8 + catalan()*half(); BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); BOOST_CHECK_CLOSE_FRACTION(L1, Q_expected, tol); auto f2 = [](Real x)->Real { Real t0 = x*x + 1; Real t1 = sqrt(t0); return atan(t1)/(t0*t1); }; Q = gauss_kronrod::integrate(f2, 0 , 1, 0, 0, &error, &L1); Q_expected = pi()/4 - pi()/root_two() + 3*atan(root_two())/root_two(); BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); BOOST_CHECK_CLOSE_FRACTION(L1, Q_expected, tol); tol = expected_error(test_ca_error_id_2); auto f5 = [](Real t)->Real { return t*t*log(t)/((t*t - 1)*(t*t*t*t + 1)); }; Q = gauss_kronrod::integrate(f5, 0, 1, 0); Q_expected = pi()*pi()*(2 - root_two())/32; BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); } template void test_three_quadrature_schemes_examples() { std::cout << "Testing integral in 'A Comparison of Three High Precision Quadrature Schemes' on type " << boost::typeindex::type_id().pretty_name() << "\n"; Real tol = expected_error(test_three_quad_error_id); Real Q; Real Q_expected; // Example 1: auto f1 = [](const Real& t)->Real { return t*boost::math::log1p(t); }; Q = gauss_kronrod::integrate(f1, 0 , 1, 0); Q_expected = half()*half(); BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); // Example 2: auto f2 = [](const Real& t)->Real { return t*t*atan(t); }; Q = gauss_kronrod::integrate(f2, 0 , 1, 0); Q_expected = (pi() -2 + 2*ln_two())/12; BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, 2 * tol); // Example 3: auto f3 = [](const Real& t)->Real { return exp(t)*cos(t); }; Q = gauss_kronrod::integrate(f3, 0, half_pi(), 0); Q_expected = boost::math::expm1(half_pi())*half(); BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); // Example 4: auto f4 = [](Real x)->Real { Real t0 = sqrt(x*x + 2); return atan(t0)/(t0*(x*x+1)); }; Q = gauss_kronrod::integrate(f4, 0 , 1, 0); Q_expected = 5*pi()*pi()/96; BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); tol = expected_error(test_three_quad_error_id_2); // Example 5: auto f5 = [](const Real& t)->Real { return sqrt(t)*log(t); }; Q = gauss_kronrod::integrate(f5, 0 , 1, 0); Q_expected = -4/ (Real) 9; BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); // Example 6: auto f6 = [](const Real& t)->Real { return sqrt(1 - t*t); }; Q = gauss_kronrod::integrate(f6, 0 , 1, 0); Q_expected = pi()/4; BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); } template void test_integration_over_real_line() { std::cout << "Testing integrals over entire real line in 'A Comparison of Three High Precision Quadrature Schemes' on type " << boost::typeindex::type_id().pretty_name() << "\n"; Real tol = expected_error(test_integration_over_real_line_error_id); Real Q; Real Q_expected; Real L1; Real error; auto f1 = [](const Real& t)->Real { return 1/(1+t*t);}; Q = gauss_kronrod::integrate(f1, -boost::math::tools::max_value(), boost::math::tools::max_value(), 0, 0, &error, &L1); Q_expected = pi(); BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol); BOOST_CHECK_CLOSE_FRACTION(L1, Q_expected, tol); } template void test_right_limit_infinite() { std::cout << "Testing right limit infinite for Gauss Kronrod in 'A Comparison of Three High Precision Quadrature Schemes' on type " << boost::typeindex::type_id().pretty_name() << "\n"; Real tol = expected_error(test_right_limit_infinite_error_id); Real Q; Real Q_expected; Real L1; Real error; // Example 11: auto f1 = [](const Real& t)->Real { return 1/(1+t*t);}; Q = gauss_kronrod::integrate(f1, 0, boost::math::tools::max_value(), 0, 0, &error, &L1); Q_expected = half_pi(); BOOST_CHECK_CLOSE(Q, Q_expected, 100*tol); auto f4 = [](const Real& t)->Real { return 1/(1+t*t); }; Q = gauss_kronrod::integrate(f4, 1, boost::math::tools::max_value(), 0, 0, &error, &L1); Q_expected = pi()/4; BOOST_CHECK_CLOSE(Q, Q_expected, 100*tol); } template void test_left_limit_infinite() { std::cout << "Testing left limit infinite for Gauss Kronrod in 'A Comparison of Three High Precision Quadrature Schemes' on type " << boost::typeindex::type_id().pretty_name() << "\n"; Real tol = expected_error(test_left_limit_infinite_error_id); Real Q; Real Q_expected; // Example 11: auto f1 = [](const Real& t)->Real { return 1/(1+t*t);}; Q = gauss_kronrod::integrate(f1, -boost::math::tools::max_value(), Real(0), 0); Q_expected = half_pi(); BOOST_CHECK_CLOSE(Q, Q_expected, 100*tol); } template void test_complex_lambert_w() { std::cout << "Testing that complex-valued integrands are integrated correctly by Gaussian quadrature on type " << boost::typeindex::type_id().pretty_name() << "\n"; typedef typename Complex::value_type Real; Real tol = 10e-9; using boost::math::constants::pi; Complex z{2, 3}; auto lw = [&z](Real v)->Complex { using std::cos; using std::sin; using std::exp; Real sinv = sin(v); Real cosv = cos(v); Real cotv = cosv/sinv; Real cscv = 1/sinv; Real t = (1-v*cotv)*(1-v*cotv) + v*v; Real x = v*cscv*exp(-v*cotv); Complex den = z + x; Complex num = t*(z/pi()); Complex res = num/den; return res; }; //N[ProductLog[2+3*I], 150] boost::math::quadrature::gauss_kronrod integrator; Complex Q = integrator.integrate(lw, (Real) 0, pi()); BOOST_CHECK_CLOSE_FRACTION(Q.real(), boost::lexical_cast("1.09007653448579084630177782678166964987102108635357778056449870727913321296238687023915522935120701763447787503167111962008709116746523970476893277703"), tol); BOOST_CHECK_CLOSE_FRACTION(Q.imag(), boost::lexical_cast("0.530139720774838801426860213574121741928705631382703178297940568794784362495390544411799468140433404536019992695815009036975117285537382995180319280835"), tol); } BOOST_AUTO_TEST_CASE(gauss_quadrature_test) { #ifdef TEST1 std::cout << "Testing 15 point approximation:\n"; test_linear(); test_quadratic(); test_ca(); test_three_quadrature_schemes_examples(); test_integration_over_real_line(); test_right_limit_infinite(); test_left_limit_infinite(); // test one case where we do not have pre-computed constants: std::cout << "Testing 17 point approximation:\n"; test_linear(); test_quadratic(); test_ca(); test_three_quadrature_schemes_examples(); test_integration_over_real_line(); test_right_limit_infinite(); test_left_limit_infinite(); test_complex_lambert_w>(); test_complex_lambert_w>(); #endif #ifdef TEST1A std::cout << "Testing 21 point approximation:\n"; test_linear(); test_quadratic(); test_ca(); test_three_quadrature_schemes_examples(); test_integration_over_real_line(); test_right_limit_infinite(); test_left_limit_infinite(); std::cout << "Testing 31 point approximation:\n"; test_linear(); test_quadratic(); test_ca(); test_three_quadrature_schemes_examples(); test_integration_over_real_line(); test_right_limit_infinite(); test_left_limit_infinite(); #endif #ifdef TEST2 std::cout << "Testing 41 point approximation:\n"; test_linear(); test_quadratic(); test_ca(); test_three_quadrature_schemes_examples(); test_integration_over_real_line(); test_right_limit_infinite(); test_left_limit_infinite(); std::cout << "Testing 51 point approximation:\n"; test_linear(); test_quadratic(); test_ca(); test_three_quadrature_schemes_examples(); test_integration_over_real_line(); test_right_limit_infinite(); test_left_limit_infinite(); #endif #ifdef TEST3 // Need at least one set of tests with expression templates turned on: std::cout << "Testing 61 point approximation:\n"; test_linear(); test_quadratic(); test_ca(); test_three_quadrature_schemes_examples(); test_integration_over_real_line(); test_right_limit_infinite(); test_left_limit_infinite(); #ifdef BOOST_HAS_FLOAT128 test_complex_lambert_w(); #endif #endif } #else int main() { return 0; } #endif