test_trapezoidal.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. * Copyright Nick Thompson, 2017
  3. * Use, modification and distribution are subject to the
  4. * Boost Software License, Version 1.0. (See accompanying file
  5. * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. */
  7. #define BOOST_TEST_MODULE trapezoidal_quadrature
  8. #include <complex>
  9. #include <boost/config.hpp>
  10. //#include <boost/multiprecision/mpc.hpp>
  11. #include <boost/test/included/unit_test.hpp>
  12. #include <boost/test/tools/floating_point_comparison.hpp>
  13. #include <boost/math/concepts/real_concept.hpp>
  14. #include <boost/math/special_functions/bessel.hpp>
  15. #include <boost/math/quadrature/trapezoidal.hpp>
  16. #include <boost/multiprecision/cpp_bin_float.hpp>
  17. #include <boost/multiprecision/cpp_dec_float.hpp>
  18. #ifdef BOOST_HAS_FLOAT128
  19. #include <boost/multiprecision/complex128.hpp>
  20. #endif
  21. using boost::multiprecision::cpp_bin_float_50;
  22. using boost::multiprecision::cpp_bin_float_100;
  23. using boost::math::quadrature::trapezoidal;
  24. // These tests come from:
  25. // https://doi.org/10.1023/A:1025524324969
  26. // "Computing special functions by using quadrature rules", Gil, Segura, and Temme.
  27. template<class Complex>
  28. void test_complex_bessel()
  29. {
  30. std::cout << "Testing that complex-valued integrands are integrated correctly by the adaptive trapezoidal routine on type " << boost::typeindex::type_id<Complex>().pretty_name() << "\n";
  31. typedef typename Complex::value_type Real;
  32. Complex z{2, 3};
  33. int n = 2;
  34. using boost::math::constants::pi;
  35. auto bessel_integrand = [&n, &z](Real theta)->Complex
  36. {
  37. using std::cos;
  38. using std::sin;
  39. Real t1 = sin(theta);
  40. Real t2 = - n*theta;
  41. Complex arg = z*t1 + t2;
  42. return cos(arg)/pi<Real>();
  43. };
  44. using boost::math::quadrature::trapezoidal;
  45. Real a = 0;
  46. Real b = pi<Real>();
  47. Complex Jnz = trapezoidal<decltype(bessel_integrand), Real>(bessel_integrand, a, b);
  48. // N[BesselJ[2, 2 + 3 I], 143]
  49. // 1.257674591970511077630764085052638490387449039392695959943027966195657681586539389134094087028482099931927725892... +
  50. // 2.318771368505683055818032722011594415038779144567369903204833213112457210243098545874099591376455981793627257060... i
  51. Real Jnzx = boost::lexical_cast<Real>("1.257674591970511077630764085052638490387449039392695959943027966195657681586539389134094087028482099931927725892");
  52. Real Jnzy = boost::lexical_cast<Real>("2.318771368505683055818032722011594415038779144567369903204833213112457210243098545874099591376455981793627257060");
  53. Real tol = 10*std::numeric_limits<Real>::epsilon();
  54. BOOST_CHECK_CLOSE_FRACTION(Jnz.real(), Jnzx, tol);
  55. BOOST_CHECK_CLOSE_FRACTION(Jnz.imag(), Jnzy, tol);
  56. }
  57. template<class Complex>
  58. void test_I0_complex()
  59. {
  60. std::cout << "Testing that complex-argument I0 is calculated correctly by the adaptive trapezoidal routine on type " << boost::typeindex::type_id<Complex>().pretty_name() << "\n";
  61. typedef typename Complex::value_type Real;
  62. Complex z{2, 3};
  63. using boost::math::constants::pi;
  64. auto I0 = [&z](Real theta)->Complex
  65. {
  66. using std::cos;
  67. using std::exp;
  68. return exp(z*cos(theta))/pi<Real>();
  69. };
  70. using boost::math::quadrature::trapezoidal;
  71. Real a = 0;
  72. Real b = pi<Real>();
  73. Complex I0z = trapezoidal<decltype(I0), Real>(I0, a, b);
  74. // N[BesselI[0, 2 + 3 I], 143]
  75. // -1.24923487960742219637619681391438589436703710701063561548156438052154090067526565701278826317992172207565649925713468090525951417141982808439560899101
  76. // 0.947983792057734776114060623981442199525094227418764823692296622398838765371662384207319492908490909109393495109183270208372778907692930675595924819922 i
  77. Real I0zx = boost::lexical_cast<Real>("-1.24923487960742219637619681391438589436703710701063561548156438052154090067526565701278826317992172207565649925713468090525951417141982808439560899101");
  78. Real I0zy = boost::lexical_cast<Real>("0.947983792057734776114060623981442199525094227418764823692296622398838765371662384207319492908490909109393495109183270208372778907692930675595924819922");
  79. Real tol = 10*std::numeric_limits<Real>::epsilon();
  80. BOOST_CHECK_CLOSE_FRACTION(I0z.real(), I0zx, tol);
  81. BOOST_CHECK_CLOSE_FRACTION(I0z.imag(), I0zy, tol);
  82. }
  83. template<class Complex>
  84. void test_erfc()
  85. {
  86. std::cout << "Testing that complex-argument erfc is calculated correctly by the adaptive trapezoidal routine on type " << boost::typeindex::type_id<Complex>().pretty_name() << "\n";
  87. typedef typename Complex::value_type Real;
  88. Complex z{2, -1};
  89. using boost::math::constants::pi;
  90. using boost::math::constants::two_pi;
  91. auto erfc = [&z](Real theta)->Complex
  92. {
  93. using std::exp;
  94. using std::tan;
  95. Real t = tan(theta/2);
  96. Complex arg = -z*z*(1+t*t);
  97. return exp(arg)/two_pi<Real>();
  98. };
  99. using boost::math::quadrature::trapezoidal;
  100. Real a = -pi<Real>();
  101. Real b = pi<Real>();
  102. Complex erfcz = trapezoidal<decltype(erfc), Real>(erfc, a, b, boost::math::tools::root_epsilon<Real>(), 17);
  103. // N[Erfc[2-i], 150]
  104. //-0.00360634272565175091291182820541914235532928536595056623793472801084629874817202107825472707423984408881473019087931573313969503235634965264302640170177
  105. // - 0.0112590060288150250764009156316482248536651598819882163212627394923365188251633729432967232423246312345152595958230197778555210858871376231770868078020 i
  106. Real erfczx = boost::lexical_cast<Real>("-0.00360634272565175091291182820541914235532928536595056623793472801084629874817202107825472707423984408881473019087931573313969503235634965264302640170177");
  107. Real erfczy = boost::lexical_cast<Real>("-0.0112590060288150250764009156316482248536651598819882163212627394923365188251633729432967232423246312345152595958230197778555210858871376231770868078020");
  108. Real tol = 5000*std::numeric_limits<Real>::epsilon();
  109. BOOST_CHECK_CLOSE_FRACTION(erfcz.real(), erfczx, tol);
  110. BOOST_CHECK_CLOSE_FRACTION(erfcz.imag(), erfczy, tol);
  111. }
  112. template<class Real>
  113. void test_constant()
  114. {
  115. std::cout << "Testing constants are integrated correctly by the adaptive trapezoidal routine on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  116. auto f = [](Real)->Real { return boost::math::constants::half<Real>(); };
  117. Real Q = trapezoidal<decltype(f), Real>(f, (Real) 0.0, (Real) 10.0);
  118. BOOST_CHECK_CLOSE(Q, 5.0, 100*std::numeric_limits<Real>::epsilon());
  119. }
  120. template<class Real>
  121. void test_rational_periodic()
  122. {
  123. using boost::math::constants::two_pi;
  124. using boost::math::constants::third;
  125. std::cout << "Testing that rational periodic functions are integrated correctly by trapezoidal rule on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  126. auto f = [](Real x)->Real { return 1/(5 - 4*cos(x)); };
  127. Real tol = 100*boost::math::tools::epsilon<Real>();
  128. Real Q = trapezoidal(f, (Real) 0.0, two_pi<Real>(), tol);
  129. BOOST_CHECK_CLOSE_FRACTION(Q, two_pi<Real>()*third<Real>(), 10*tol);
  130. }
  131. template<class Real>
  132. void test_bump_function()
  133. {
  134. std::cout << "Testing that bump functions are integrated correctly by trapezoidal rule on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  135. auto f = [](Real x)->Real {
  136. if( x>= 1 || x <= -1)
  137. {
  138. return (Real) 0;
  139. }
  140. return (Real) exp(-(Real) 1/(1-x*x));
  141. };
  142. Real tol = boost::math::tools::epsilon<Real>();
  143. Real Q = trapezoidal(f, (Real) -1, (Real) 1, tol);
  144. // 2*NIntegrate[Exp[-(1/(1 - x^2))], {x, 0, 1}, WorkingPrecision -> 210]
  145. Real Q_exp = boost::lexical_cast<Real>("0.44399381616807943782304892117055266376120178904569749730748455394704");
  146. BOOST_CHECK_CLOSE_FRACTION(Q, Q_exp, 30*tol);
  147. }
  148. template<class Real>
  149. void test_zero_function()
  150. {
  151. std::cout << "Testing that zero functions are integrated correctly by trapezoidal rule on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  152. auto f = [](Real)->Real { return (Real) 0;};
  153. Real tol = 100* boost::math::tools::epsilon<Real>();
  154. Real Q = trapezoidal(f, (Real) -1, (Real) 1, tol);
  155. BOOST_CHECK_SMALL(Q, 100*tol);
  156. }
  157. template<class Real>
  158. void test_sinsq()
  159. {
  160. std::cout << "Testing that sin(x)^2 is integrated correctly by the trapezoidal rule on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  161. auto f = [](Real x)->Real { return sin(10*x)*sin(10*x); };
  162. Real tol = 100* boost::math::tools::epsilon<Real>();
  163. Real Q = trapezoidal(f, (Real) 0, (Real) boost::math::constants::pi<Real>(), tol);
  164. BOOST_CHECK_CLOSE_FRACTION(Q, boost::math::constants::half_pi<Real>(), tol);
  165. }
  166. template<class Real>
  167. void test_slowly_converging()
  168. {
  169. using std::sqrt;
  170. std::cout << "Testing that non-periodic functions are correctly integrated by the trapezoidal rule, even if slowly, on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  171. // This function is not periodic, so it should not be fast to converge:
  172. auto f = [](Real x)->Real { using std::sqrt; return sqrt(1 - x*x); };
  173. Real tol = sqrt(sqrt(boost::math::tools::epsilon<Real>()));
  174. Real error_estimate;
  175. Real Q = trapezoidal(f, (Real) 0, (Real) 1, tol, 15, &error_estimate);
  176. BOOST_CHECK_CLOSE_FRACTION(Q, boost::math::constants::half_pi<Real>()/2, 10*tol);
  177. }
  178. template<class Real>
  179. void test_rational_sin()
  180. {
  181. using std::pow;
  182. using std::sin;
  183. using boost::math::constants::two_pi;
  184. using boost::math::constants::half;
  185. std::cout << "Testing that a rational sin function is integrated correctly by the trapezoidal rule on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  186. Real a = 5;
  187. auto f = [=](Real x)->Real { using std::sin; Real t = a + sin(x); return 1.0f / (t*t); };
  188. Real expected = two_pi<Real>()*a/pow(a*a - 1, 3*half<Real>());
  189. Real tol = 100* boost::math::tools::epsilon<Real>();
  190. Real Q = trapezoidal(f, (Real) 0, (Real) boost::math::constants::two_pi<Real>(), tol);
  191. BOOST_CHECK_CLOSE_FRACTION(Q, expected, tol);
  192. }
  193. BOOST_AUTO_TEST_CASE(trapezoidal_quadrature)
  194. {
  195. test_constant<float>();
  196. test_constant<double>();
  197. test_constant<long double>();
  198. test_constant<boost::math::concepts::real_concept>();
  199. test_constant<cpp_bin_float_50>();
  200. test_constant<cpp_bin_float_100>();
  201. test_rational_periodic<float>();
  202. test_rational_periodic<double>();
  203. test_rational_periodic<long double>();
  204. test_rational_periodic<boost::math::concepts::real_concept>();
  205. test_rational_periodic<cpp_bin_float_50>();
  206. test_rational_periodic<cpp_bin_float_100>();
  207. test_bump_function<float>();
  208. test_bump_function<double>();
  209. test_bump_function<long double>();
  210. test_rational_periodic<boost::math::concepts::real_concept>();
  211. test_rational_periodic<cpp_bin_float_50>();
  212. test_zero_function<float>();
  213. test_zero_function<double>();
  214. test_zero_function<long double>();
  215. test_zero_function<boost::math::concepts::real_concept>();
  216. test_zero_function<cpp_bin_float_50>();
  217. test_zero_function<cpp_bin_float_100>();
  218. test_sinsq<float>();
  219. test_sinsq<double>();
  220. test_sinsq<long double>();
  221. test_sinsq<boost::math::concepts::real_concept>();
  222. test_sinsq<cpp_bin_float_50>();
  223. test_sinsq<cpp_bin_float_100>();
  224. test_slowly_converging<float>();
  225. test_slowly_converging<double>();
  226. test_slowly_converging<long double>();
  227. test_slowly_converging<boost::math::concepts::real_concept>();
  228. test_rational_sin<float>();
  229. test_rational_sin<double>();
  230. test_rational_sin<long double>();
  231. //test_rational_sin<boost::math::concepts::real_concept>();
  232. test_rational_sin<cpp_bin_float_50>();
  233. test_complex_bessel<std::complex<float>>();
  234. test_complex_bessel<std::complex<double>>();
  235. test_complex_bessel<std::complex<long double>>();
  236. //test_complex_bessel<boost::multiprecision::mpc_complex_100>();
  237. test_I0_complex<std::complex<float>>();
  238. test_I0_complex<std::complex<double>>();
  239. test_I0_complex<std::complex<long double>>();
  240. //test_I0_complex<boost::multiprecision::mpc_complex_100>();
  241. test_erfc<std::complex<float>>();
  242. test_erfc<std::complex<double>>();
  243. test_erfc<std::complex<long double>>();
  244. //test_erfc<boost::multiprecision::number<boost::multiprecision::mpc_complex_backend<20>>>();
  245. //test_erfc<boost::multiprecision::number<boost::multiprecision::mpc_complex_backend<30>>>();
  246. //test_erfc<boost::multiprecision::mpc_complex_50>();
  247. //test_erfc<boost::multiprecision::mpc_complex_100>();
  248. #ifdef BOOST_HAS_FLOAT128
  249. test_complex_bessel<boost::multiprecision::complex128>();
  250. test_I0_complex<boost::multiprecision::complex128>();
  251. test_erfc<boost::multiprecision::complex128>();
  252. #endif
  253. }