ooura_fourier_integral_test.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // Copyright Nick Thompson, 2019
  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 test_ooura_fourier_transform
  7. #include <cmath>
  8. #include <iostream>
  9. #include <boost/type_index.hpp>
  10. #include <boost/test/included/unit_test.hpp>
  11. #include <boost/test/tools/floating_point_comparison.hpp>
  12. #include <boost/math/quadrature/ooura_fourier_integrals.hpp>
  13. #include <boost/multiprecision/cpp_bin_float.hpp>
  14. using boost::math::quadrature::ooura_fourier_sin;
  15. using boost::math::quadrature::ooura_fourier_cos;
  16. using boost::math::constants::pi;
  17. float float_tol = 10*std::numeric_limits<float>::epsilon();
  18. ooura_fourier_sin<float> float_sin_integrator(float_tol);
  19. double double_tol = 10*std::numeric_limits<double>::epsilon();
  20. ooura_fourier_sin<double> double_sin_integrator(double_tol);
  21. long double long_double_tol = 10*std::numeric_limits<long double>::epsilon();
  22. ooura_fourier_sin<long double> long_double_sin_integrator(long_double_tol);
  23. template<class Real>
  24. auto get_sin_integrator() {
  25. if constexpr (std::is_same_v<Real, float>) {
  26. return float_sin_integrator;
  27. }
  28. if constexpr (std::is_same_v<Real, double>) {
  29. return double_sin_integrator;
  30. }
  31. if constexpr (std::is_same_v<Real, long double>) {
  32. return long_double_sin_integrator;
  33. }
  34. }
  35. ooura_fourier_cos<float> float_cos_integrator(float_tol);
  36. ooura_fourier_cos<double> double_cos_integrator(double_tol);
  37. ooura_fourier_cos<long double> long_double_cos_integrator(long_double_tol);
  38. template<class Real>
  39. auto get_cos_integrator() {
  40. if constexpr (std::is_same_v<Real, float>) {
  41. return float_cos_integrator;
  42. }
  43. if constexpr (std::is_same_v<Real, double>) {
  44. return double_cos_integrator;
  45. }
  46. if constexpr (std::is_same_v<Real, long double>) {
  47. return long_double_cos_integrator;
  48. }
  49. }
  50. template<class Real>
  51. void test_ooura_eta()
  52. {
  53. using boost::math::quadrature::detail::ooura_eta;
  54. std::cout << "Testing eta function on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  55. {
  56. Real x = 0;
  57. Real alpha = 7;
  58. auto [eta, eta_prime] = ooura_eta(x, alpha);
  59. BOOST_CHECK_SMALL(eta, (std::numeric_limits<Real>::min)());
  60. BOOST_CHECK_CLOSE_FRACTION(eta_prime, 2 + alpha + Real(1)/Real(4), 10*std::numeric_limits<Real>::epsilon());
  61. }
  62. {
  63. Real alpha = 4;
  64. for (Real z = 0.125; z < 500; z += 0.125) {
  65. Real x = std::log(z);
  66. auto [eta, eta_prime] = ooura_eta(x, alpha);
  67. BOOST_CHECK_CLOSE_FRACTION(eta, 2*x + alpha*(1-1/z) + (z-1)/4, 10*std::numeric_limits<Real>::epsilon());
  68. BOOST_CHECK_CLOSE_FRACTION(eta_prime, 2 + alpha/z + z/4, 10*std::numeric_limits<Real>::epsilon());
  69. }
  70. }
  71. }
  72. template<class Real>
  73. void test_ooura_sin_nodes_and_weights()
  74. {
  75. using boost::math::quadrature::detail::ooura_sin_node_and_weight;
  76. using boost::math::quadrature::detail::ooura_eta;
  77. std::cout << "Testing nodes and weights on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  78. {
  79. long n = 1;
  80. Real alpha = 1;
  81. Real h = 1;
  82. auto [node, weight] = ooura_sin_node_and_weight(n, h, alpha);
  83. Real expected_node = pi<Real>()/(1-exp(-ooura_eta(n*h, alpha).first));
  84. BOOST_CHECK_CLOSE_FRACTION(node, expected_node,10*std::numeric_limits<Real>::epsilon());
  85. }
  86. }
  87. template<class Real>
  88. void test_ooura_alpha() {
  89. std::cout << "Testing Ooura alpha on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  90. using std::sqrt;
  91. using std::log1p;
  92. using boost::math::quadrature::detail::calculate_ooura_alpha;
  93. Real alpha = calculate_ooura_alpha(Real(1));
  94. Real expected = 1/sqrt(16 + 4*log1p(pi<Real>()));
  95. BOOST_CHECK_CLOSE_FRACTION(alpha, expected, 10*std::numeric_limits<Real>::epsilon());
  96. }
  97. void test_node_weight_precision_agreement()
  98. {
  99. using std::abs;
  100. using boost::math::quadrature::detail::ooura_sin_node_and_weight;
  101. using boost::math::quadrature::detail::ooura_eta;
  102. using boost::multiprecision::cpp_bin_float_quad;
  103. std::cout << "Testing agreement in two different precisions of nodes and weights\n";
  104. cpp_bin_float_quad alpha_quad = 1;
  105. long int_max = 128;
  106. cpp_bin_float_quad h_quad = 1/cpp_bin_float_quad(int_max);
  107. double alpha_dbl = 1;
  108. double h_dbl = static_cast<double>(h_quad);
  109. std::cout << std::fixed;
  110. for (long n = -1; n > -6*int_max; --n) {
  111. auto [node_dbl, weight_dbl] = ooura_sin_node_and_weight(n, h_dbl, alpha_dbl);
  112. auto p = ooura_sin_node_and_weight(n, h_quad, alpha_quad);
  113. double node_quad = static_cast<double>(p.first);
  114. double weight_quad = static_cast<double>(p.second);
  115. auto node_dist = abs(boost::math::float_distance(node_quad, node_dbl));
  116. if ( (weight_quad < 0 && weight_dbl > 0) || (weight_dbl < 0 && weight_quad > 0) ){
  117. std::cout << "Weights at different precisions have different signs!\n";
  118. } else {
  119. auto weight_dist = abs(boost::math::float_distance(weight_quad, weight_dbl));
  120. if (weight_dist > 100) {
  121. std::cout << std::fixed;
  122. std::cout <<"n =" << n << ", x = " << n*h_dbl << ", node distance = " << node_dist << ", weight distance = " << weight_dist << "\n";
  123. std::cout << std::scientific;
  124. std::cout << "computed weight = " << weight_dbl << ", actual weight = " << weight_quad << "\n";
  125. }
  126. }
  127. }
  128. }
  129. template<class Real>
  130. void test_sinc()
  131. {
  132. std::cout << "Testing sinc integral on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  133. using std::numeric_limits;
  134. Real tol = 50*numeric_limits<Real>::epsilon();
  135. auto integrator = get_sin_integrator<Real>();
  136. auto f = [](Real x)->Real { return 1/x; };
  137. Real omega = 1;
  138. while (omega < 10)
  139. {
  140. auto [Is, err] = integrator.integrate(f, omega);
  141. BOOST_CHECK_CLOSE_FRACTION(Is, pi<Real>()/2, tol);
  142. auto [Isn, errn] = integrator.integrate(f, -omega);
  143. BOOST_CHECK_CLOSE_FRACTION(Isn, -pi<Real>()/2, tol);
  144. omega += 1;
  145. }
  146. }
  147. template<class Real>
  148. void test_exp()
  149. {
  150. std::cout << "Testing exponential integral on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  151. using std::exp;
  152. using std::numeric_limits;
  153. Real tol = 50*numeric_limits<Real>::epsilon();
  154. auto integrator = get_sin_integrator<Real>();
  155. auto f = [](Real x)->Real {return exp(-x);};
  156. Real omega = 1;
  157. while (omega < 5)
  158. {
  159. auto [Is, err] = integrator.integrate(f, omega);
  160. Real exact = omega/(1+omega*omega);
  161. BOOST_CHECK_CLOSE_FRACTION(Is, exact, tol);
  162. omega += 1;
  163. }
  164. }
  165. template<class Real>
  166. void test_root()
  167. {
  168. std::cout << "Testing integral of sin(kx)/sqrt(x) on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  169. using std::sqrt;
  170. using std::numeric_limits;
  171. Real tol = 10*numeric_limits<Real>::epsilon();
  172. auto integrator = get_sin_integrator<Real>();
  173. auto f = [](Real x)->Real { return 1/sqrt(x);};
  174. Real omega = 1;
  175. while (omega < 5) {
  176. auto [Is, err] = integrator.integrate(f, omega);
  177. Real exact = sqrt(pi<Real>()/(2*omega));
  178. BOOST_CHECK_CLOSE_FRACTION(Is, exact, 10*tol);
  179. omega += 1;
  180. }
  181. }
  182. // See: https://scicomp.stackexchange.com/questions/32790/numerical-evaluation-of-highly-oscillatory-integral/32799#32799
  183. template<class Real>
  184. Real asymptotic(Real lambda) {
  185. using std::sin;
  186. using std::cos;
  187. using boost::math::constants::pi;
  188. Real I1 = cos(lambda - pi<Real>()/4)*sqrt(2*pi<Real>()/lambda);
  189. Real I2 = sin(lambda - pi<Real>()/4)*sqrt(2*pi<Real>()/(lambda*lambda*lambda))/8;
  190. return I1 + I2;
  191. }
  192. template<class Real>
  193. void test_double_osc()
  194. {
  195. std::cout << "Testing double oscillation on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  196. using std::sqrt;
  197. using std::numeric_limits;
  198. auto integrator = get_sin_integrator<Real>();
  199. Real lambda = 7;
  200. auto f = [&lambda](Real x)->Real { return cos(lambda*cos(x))/x; };
  201. Real omega = 1;
  202. auto [Is, err] = integrator.integrate(f, omega);
  203. Real exact = asymptotic(lambda);
  204. BOOST_CHECK_CLOSE_FRACTION(2*Is, exact, 0.05);
  205. }
  206. template<class Real>
  207. void test_zero_integrand()
  208. {
  209. // Make sure relative error tolerance doesn't break on zero integrand:
  210. std::cout << "Testing zero integrand on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  211. using std::sqrt;
  212. using std::numeric_limits;
  213. auto integrator = get_sin_integrator<Real>();
  214. auto f = [](Real /* x */)->Real { return Real(0); };
  215. Real omega = 1;
  216. auto [Is, err] = integrator.integrate(f, omega);
  217. Real exact = 0;
  218. BOOST_CHECK_EQUAL(Is, exact);
  219. }
  220. // This works, but doesn't recover the precision you want in a unit test:
  221. // template<class Real>
  222. // void test_log()
  223. // {
  224. // std::cout << "Testing integral of log(x)sin(x) on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  225. // using std::log;
  226. // using std::exp;
  227. // using std::numeric_limits;
  228. // using boost::math::constants::euler;
  229. // Real tol = 1000*numeric_limits<Real>::epsilon();
  230. // auto f = [](Real x)->Real { return exp(-100*numeric_limits<Real>::epsilon()*x)*log(x);};
  231. // Real omega = 1;
  232. // Real Is = ooura_fourier_sin<decltype(f), Real>(f, omega, sqrt(numeric_limits<Real>::epsilon())/100);
  233. // BOOST_CHECK_CLOSE_FRACTION(Is, -euler<Real>(), tol);
  234. // }
  235. template<class Real>
  236. void test_cos_integral1()
  237. {
  238. std::cout << "Testing integral of cos(x)/(x*x+1) on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  239. using std::exp;
  240. using boost::math::constants::half_pi;
  241. using boost::math::constants::e;
  242. using std::numeric_limits;
  243. Real tol = 10*numeric_limits<Real>::epsilon();
  244. auto integrator = get_cos_integrator<Real>();
  245. auto f = [](Real x)->Real { return 1/(x*x+1);};
  246. Real omega = 1;
  247. auto [Is, err] = integrator.integrate(f, omega);
  248. Real exact = half_pi<Real>()/e<Real>();
  249. BOOST_CHECK_CLOSE_FRACTION(Is, exact, tol);
  250. }
  251. template<class Real>
  252. void test_cos_integral2()
  253. {
  254. std::cout << "Testing integral of exp(-a*x) on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  255. using std::exp;
  256. using boost::math::constants::half_pi;
  257. using boost::math::constants::e;
  258. using std::numeric_limits;
  259. Real tol = 10*numeric_limits<Real>::epsilon();
  260. auto integrator = get_cos_integrator<Real>();
  261. for (Real a = 1; a < 5; ++a) {
  262. auto f = [&a](Real x)->Real { return exp(-a*x);};
  263. for(Real omega = 1; omega < 5; ++omega) {
  264. auto [Is, err] = integrator.integrate(f, omega);
  265. Real exact = a/(a*a+omega*omega);
  266. BOOST_CHECK_CLOSE_FRACTION(Is, exact, 10*tol);
  267. }
  268. }
  269. }
  270. template<class Real>
  271. void test_nodes()
  272. {
  273. std::cout << "Testing nodes and weights on type " << boost::typeindex::type_id<Real>().pretty_name() << "\n";
  274. auto sin_integrator = get_sin_integrator<Real>();
  275. auto const & big_nodes = sin_integrator.big_nodes();
  276. for (auto & node_row : big_nodes) {
  277. Real t0 = node_row[0];
  278. for (size_t i = 1; i < node_row.size(); ++i) {
  279. Real t1 = node_row[i];
  280. BOOST_CHECK(t1 > t0);
  281. t0 = t1;
  282. }
  283. }
  284. auto const & little_nodes = sin_integrator.little_nodes();
  285. for (auto & node_row : little_nodes) {
  286. Real t0 = node_row[0];
  287. for (size_t i = 1; i < node_row.size(); ++i) {
  288. Real t1 = node_row[i];
  289. BOOST_CHECK(t1 < t0);
  290. t0 = t1;
  291. }
  292. }
  293. }
  294. BOOST_AUTO_TEST_CASE(ooura_fourier_transform_test)
  295. {
  296. test_cos_integral1<float>();
  297. test_cos_integral1<double>();
  298. test_cos_integral1<long double>();
  299. test_cos_integral2<float>();
  300. test_cos_integral2<double>();
  301. test_cos_integral2<long double>();
  302. //test_node_weight_precision_agreement();
  303. test_zero_integrand<float>();
  304. test_zero_integrand<double>();
  305. test_ooura_eta<float>();
  306. test_ooura_eta<double>();
  307. test_ooura_eta<long double>();
  308. test_ooura_sin_nodes_and_weights<float>();
  309. test_ooura_sin_nodes_and_weights<double>();
  310. test_ooura_sin_nodes_and_weights<long double>();
  311. test_ooura_alpha<float>();
  312. test_ooura_alpha<double>();
  313. test_ooura_alpha<long double>();
  314. test_sinc<float>();
  315. test_sinc<double>();
  316. test_sinc<long double>();
  317. test_exp<float>();
  318. test_exp<double>();
  319. test_exp<long double>();
  320. test_root<float>();
  321. test_root<double>();
  322. test_double_osc<float>();
  323. test_double_osc<double>();
  324. // Takes too long!
  325. //test_double_osc<long double>();
  326. // This test should be last:
  327. test_nodes<float>();
  328. test_nodes<double>();
  329. test_nodes<long double>();
  330. }