sinh_sinh_quadrature_test.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // Copyright Nick Thompson, 2017
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #define BOOST_TEST_MODULE sinh_sinh_quadrature_test
  7. #include <complex>
  8. #include <boost/multiprecision/cpp_complex.hpp>
  9. #include <boost/math/concepts/real_concept.hpp>
  10. #include <boost/test/included/unit_test.hpp>
  11. #include <boost/test/tools/floating_point_comparison.hpp>
  12. #include <boost/math/quadrature/sinh_sinh.hpp>
  13. #include <boost/math/special_functions/sinc.hpp>
  14. #include <boost/multiprecision/cpp_bin_float.hpp>
  15. #if !BOOST_WORKAROUND(BOOST_MSVC, < 1900)
  16. // MSVC-12 has problems if we include 2 different multiprecision types in the same program,
  17. // basically random things stop compiling, even though they work fine in isolation :(
  18. #include <boost/multiprecision/cpp_dec_float.hpp>
  19. #endif
  20. using std::expm1;
  21. using std::exp;
  22. using std::sin;
  23. using std::cos;
  24. using std::atan;
  25. using std::tan;
  26. using std::log;
  27. using std::log1p;
  28. using std::asinh;
  29. using std::atanh;
  30. using std::sqrt;
  31. using std::isnormal;
  32. using std::abs;
  33. using std::sinh;
  34. using std::tanh;
  35. using std::cosh;
  36. using std::pow;
  37. using std::string;
  38. using boost::multiprecision::cpp_bin_float_quad;
  39. using boost::math::quadrature::sinh_sinh;
  40. using boost::math::constants::pi;
  41. using boost::math::constants::pi_sqr;
  42. using boost::math::constants::half_pi;
  43. using boost::math::constants::two_div_pi;
  44. using boost::math::constants::half;
  45. using boost::math::constants::third;
  46. using boost::math::constants::half;
  47. using boost::math::constants::third;
  48. using boost::math::constants::catalan;
  49. using boost::math::constants::ln_two;
  50. using boost::math::constants::root_two;
  51. using boost::math::constants::root_two_pi;
  52. using boost::math::constants::root_pi;
  53. //
  54. // Code for generating the coefficients:
  55. //
  56. template <class T>
  57. void print_levels(const T& v, const char* suffix)
  58. {
  59. std::cout << "{\n";
  60. for (unsigned i = 0; i < v.size(); ++i)
  61. {
  62. std::cout << " { ";
  63. for (unsigned j = 0; j < v[i].size(); ++j)
  64. {
  65. std::cout << v[i][j] << suffix << ", ";
  66. }
  67. std::cout << "},\n";
  68. }
  69. std::cout << " };\n";
  70. }
  71. template <class T>
  72. void print_levels(const std::pair<T, T>& p, const char* suffix = "")
  73. {
  74. std::cout << " static const std::vector<std::vector<Real> > abscissa = ";
  75. print_levels(p.first, suffix);
  76. std::cout << " static const std::vector<std::vector<Real> > weights = ";
  77. print_levels(p.second, suffix);
  78. }
  79. template <class Real, class TargetType>
  80. std::pair<std::vector<std::vector<Real>>, std::vector<std::vector<Real>> > generate_constants(unsigned max_rows)
  81. {
  82. using boost::math::constants::half_pi;
  83. using boost::math::constants::two_div_pi;
  84. using boost::math::constants::pi;
  85. auto g = [](Real t) { return sinh(half_pi<Real>()*sinh(t)); };
  86. auto w = [](Real t) { return cosh(t)*half_pi<Real>()*cosh(half_pi<Real>()*sinh(t)); };
  87. std::vector<std::vector<Real>> abscissa, weights;
  88. std::vector<Real> temp;
  89. Real t_max = log(2 * two_div_pi<Real>()*log(2 * two_div_pi<Real>()*sqrt(boost::math::tools::max_value<TargetType>())));
  90. std::cout << "m_t_max = " << t_max << ";\n";
  91. Real h = 1;
  92. for (Real t = 1; t < t_max; t += h)
  93. {
  94. temp.push_back(g(t));
  95. }
  96. abscissa.push_back(temp);
  97. temp.clear();
  98. for (Real t = 1; t < t_max; t += h)
  99. {
  100. temp.push_back(w(t * h));
  101. }
  102. weights.push_back(temp);
  103. temp.clear();
  104. for (unsigned row = 1; row < max_rows; ++row)
  105. {
  106. h /= 2;
  107. for (Real t = h; t < t_max; t += 2 * h)
  108. temp.push_back(g(t));
  109. abscissa.push_back(temp);
  110. temp.clear();
  111. }
  112. h = 1;
  113. for (unsigned row = 1; row < max_rows; ++row)
  114. {
  115. h /= 2;
  116. for (Real t = h; t < t_max; t += 2 * h)
  117. temp.push_back(w(t));
  118. weights.push_back(temp);
  119. temp.clear();
  120. }
  121. return std::make_pair(abscissa, weights);
  122. }
  123. template<class Real>
  124. void test_nr_examples()
  125. {
  126. std::cout << "Testing type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  127. Real integration_limit = sqrt(boost::math::tools::epsilon<Real>());
  128. Real tol = 10 * boost::math::tools::epsilon<Real>();
  129. std::cout << std::setprecision(std::numeric_limits<Real>::digits10);
  130. Real Q;
  131. Real Q_expected;
  132. Real L1;
  133. Real error;
  134. sinh_sinh<Real> integrator(10);
  135. auto f0 = [](Real)->Real { return (Real) 0; };
  136. Q = integrator.integrate(f0, integration_limit, &error, &L1);
  137. Q_expected = 0;
  138. BOOST_CHECK_SMALL(Q, tol);
  139. BOOST_CHECK_SMALL(L1, tol);
  140. // In spite of the poles at \pm i, we still get a doubling of the correct digits at each level of refinement.
  141. auto f1 = [](const Real& t)->Real { return 1/(1+t*t); };
  142. Q = integrator.integrate(f1, integration_limit, &error, &L1);
  143. Q_expected = pi<Real>();
  144. BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
  145. BOOST_CHECK_CLOSE_FRACTION(L1, Q_expected, tol);
  146. #if defined(BOOST_MSVC) && (BOOST_MSVC < 1900)
  147. auto f2 = [](const Real& x)->Real { return fabs(x) > boost::math::tools::log_max_value<Real>() ? 0 : exp(-x*x); };
  148. #else
  149. auto f2 = [](const Real& x)->Real { return exp(-x*x); };
  150. #endif
  151. Q = integrator.integrate(f2, integration_limit, &error, &L1);
  152. Q_expected = root_pi<Real>();
  153. BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
  154. BOOST_CHECK_CLOSE_FRACTION(L1, Q_expected, tol);
  155. auto f5 = [](const Real& t)->Real { return 1/cosh(t);};
  156. Q = integrator.integrate(f5, integration_limit, &error, &L1);
  157. Q_expected = pi<Real>();
  158. BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
  159. BOOST_CHECK_CLOSE_FRACTION(L1, Q_expected, tol);
  160. // This oscillatory integral has rapid convergence because the oscillations get swamped by the exponential growth of the denominator,
  161. // none the less the error is slightly higher than for the other cases:
  162. tol *= 10;
  163. auto f8 = [](const Real& t)->Real { return cos(t)/cosh(t);};
  164. Q = integrator.integrate(f8, integration_limit, &error, &L1);
  165. Q_expected = pi<Real>()/cosh(half_pi<Real>());
  166. BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
  167. // Try again with progressively fewer arguments:
  168. Q = integrator.integrate(f8, integration_limit);
  169. BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
  170. Q = integrator.integrate(f8);
  171. BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
  172. }
  173. // Test formulas for in the CRC Handbook of Mathematical functions, 32nd edition.
  174. template<class Real>
  175. void test_crc()
  176. {
  177. std::cout << "Testing CRC formulas on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  178. Real integration_limit = sqrt(boost::math::tools::epsilon<Real>());
  179. Real tol = 10 * boost::math::tools::epsilon<Real>();
  180. std::cout << std::setprecision(std::numeric_limits<Real>::digits10);
  181. Real Q;
  182. Real Q_expected;
  183. Real L1;
  184. Real error;
  185. sinh_sinh<Real> integrator(10);
  186. // CRC Definite integral 698:
  187. auto f0 = [](Real x)->Real {
  188. using std::sinh;
  189. if(x == 0) {
  190. return (Real) 1;
  191. }
  192. return x/sinh(x);
  193. };
  194. Q = integrator.integrate(f0, integration_limit, &error, &L1);
  195. Q_expected = pi_sqr<Real>()/2;
  196. BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
  197. BOOST_CHECK_CLOSE_FRACTION(L1, Q_expected, tol);
  198. // CRC Definite integral 695:
  199. auto f1 = [](Real x)->Real {
  200. using std::sin; using std::sinh;
  201. if(x == 0) {
  202. return (Real) 1;
  203. }
  204. return (Real) sin(x)/sinh(x);
  205. };
  206. Q = integrator.integrate(f1, integration_limit, &error, &L1);
  207. Q_expected = pi<Real>()*tanh(half_pi<Real>());
  208. BOOST_CHECK_CLOSE_FRACTION(Q, Q_expected, tol);
  209. }
  210. template<class Complex>
  211. void test_dirichlet_eta()
  212. {
  213. typedef typename Complex::value_type Real;
  214. std::cout << "Testing Dirichlet eta function on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  215. Real tol = 10 * boost::math::tools::epsilon<Real>();
  216. Complex Q;
  217. sinh_sinh<Real> integrator(10);
  218. //https://en.wikipedia.org/wiki/Dirichlet_eta_function, integral representations:
  219. Complex z = {1,1};
  220. auto eta = [&z](Real t)->Complex {
  221. using std::exp;
  222. using std::pow;
  223. using boost::math::constants::pi;
  224. Complex i = {0,1};
  225. Complex num = pow((Real)1/ (Real)2 + i*t, -z);
  226. Real denom = exp(pi<Real>()*t) + exp(-pi<Real>()*t);
  227. Complex res = num/denom;
  228. return res;
  229. };
  230. Q = integrator.integrate(eta);
  231. // N[DirichletEta[1 + I], 150]
  232. Complex Q_expected = {boost::lexical_cast<Real>("0.726559775062463263201495728547241386311129502735725787103568290594808442332084045617744978600192784188182345866652233650512117834307254514480657408096"),
  233. boost::lexical_cast<Real>("0.158095863901207324355426285544321998253687969756843115763682522207208309489794631247865357375538028170751576870244296106203144195376645765556607038775")};
  234. BOOST_CHECK_CLOSE_FRACTION(Q.real(), Q_expected.real(), tol);
  235. BOOST_CHECK_CLOSE_FRACTION(Q.imag(), Q_expected.imag(), tol);
  236. }
  237. BOOST_AUTO_TEST_CASE(sinh_sinh_quadrature_test)
  238. {
  239. //
  240. // Uncomment the following to print out the coefficients:
  241. //
  242. /*
  243. std::cout << std::scientific << std::setprecision(8);
  244. print_levels(generate_constants<cpp_bin_float_100, float>(8), "f");
  245. std::cout << std::setprecision(18);
  246. print_levels(generate_constants<cpp_bin_float_100, double>(8), "");
  247. std::cout << std::setprecision(35);
  248. print_levels(generate_constants<cpp_bin_float_100, cpp_bin_float_quad>(8), "L");
  249. */
  250. test_nr_examples<float>();
  251. test_nr_examples<double>();
  252. #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  253. test_nr_examples<long double>();
  254. #endif
  255. test_nr_examples<cpp_bin_float_quad>();
  256. #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  257. test_nr_examples<boost::math::concepts::real_concept>();
  258. #endif
  259. #if !BOOST_WORKAROUND(BOOST_MSVC, < 1900)
  260. test_nr_examples<boost::multiprecision::cpp_dec_float_50>();
  261. #endif
  262. test_crc<float>();
  263. test_crc<double>();
  264. test_dirichlet_eta<std::complex<double>>();
  265. #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  266. test_crc<long double>();
  267. test_dirichlet_eta<std::complex<long double>>();
  268. #endif
  269. test_crc<cpp_bin_float_quad>();
  270. test_dirichlet_eta<boost::multiprecision::cpp_complex_quad>();
  271. #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  272. test_crc<boost::math::concepts::real_concept>();
  273. #endif
  274. #if !BOOST_WORKAROUND(BOOST_MSVC, < 1900)
  275. test_crc<boost::multiprecision::cpp_dec_float_50>();
  276. #endif
  277. }