ooura_fourier_integrals_detail.hpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  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. #ifndef BOOST_MATH_QUADRATURE_DETAIL_OOURA_FOURIER_INTEGRALS_DETAIL_HPP
  7. #define BOOST_MATH_QUADRATURE_DETAIL_OOURA_FOURIER_INTEGRALS_DETAIL_HPP
  8. #include <utility> // for std::pair.
  9. #include <mutex>
  10. #include <atomic>
  11. #include <vector>
  12. #include <iostream>
  13. #include <boost/math/special_functions/expm1.hpp>
  14. #include <boost/math/special_functions/sin_pi.hpp>
  15. #include <boost/math/special_functions/cos_pi.hpp>
  16. #include <boost/math/constants/constants.hpp>
  17. namespace boost { namespace math { namespace quadrature { namespace detail {
  18. // Ooura and Mori, A robust double exponential formula for Fourier-type integrals,
  19. // eta is the argument to the exponential in equation 3.3:
  20. template<class Real>
  21. std::pair<Real, Real> ooura_eta(Real x, Real alpha) {
  22. using std::expm1;
  23. using std::exp;
  24. using std::abs;
  25. Real expx = exp(x);
  26. Real eta_prime = 2 + alpha/expx + expx/4;
  27. Real eta;
  28. // This is the fast branch:
  29. if (abs(x) > 0.125) {
  30. eta = 2*x - alpha*(1/expx - 1) + (expx - 1)/4;
  31. }
  32. else {// this is the slow branch using expm1 for small x:
  33. eta = 2*x - alpha*expm1(-x) + expm1(x)/4;
  34. }
  35. return {eta, eta_prime};
  36. }
  37. // Ooura and Mori, A robust double exponential formula for Fourier-type integrals,
  38. // equation 3.6:
  39. template<class Real>
  40. Real calculate_ooura_alpha(Real h)
  41. {
  42. using boost::math::constants::pi;
  43. using std::log1p;
  44. using std::sqrt;
  45. Real x = sqrt(16 + 4*log1p(pi<Real>()/h)/h);
  46. return 1/x;
  47. }
  48. template<class Real>
  49. std::pair<Real, Real> ooura_sin_node_and_weight(long n, Real h, Real alpha)
  50. {
  51. using std::expm1;
  52. using std::exp;
  53. using std::abs;
  54. using boost::math::constants::pi;
  55. using std::isnan;
  56. if (n == 0) {
  57. // Equation 44 of https://arxiv.org/pdf/0911.4796.pdf
  58. // Fourier Transform of the Stretched Exponential Function: Analytic Error Bounds,
  59. // Double Exponential Transform, and Open-Source Implementation,
  60. // Joachim Wuttke,
  61. // The C library libkww provides functions to compute the Kohlrausch-Williams-Watts function,
  62. // the Laplace-Fourier transform of the stretched (or compressed) exponential function exp(-t^beta)
  63. // for exponent beta between 0.1 and 1.9 with sixteen decimal digits accuracy.
  64. Real eta_prime_0 = Real(2) + alpha + Real(1)/Real(4);
  65. Real node = pi<Real>()/(eta_prime_0*h);
  66. Real weight = pi<Real>()*boost::math::sin_pi(1/(eta_prime_0*h));
  67. Real eta_dbl_prime = -alpha + Real(1)/Real(4);
  68. Real phi_prime_0 = (1 - eta_dbl_prime/(eta_prime_0*eta_prime_0))/2;
  69. weight *= phi_prime_0;
  70. return {node, weight};
  71. }
  72. Real x = n*h;
  73. auto p = ooura_eta(x, alpha);
  74. auto eta = p.first;
  75. auto eta_prime = p.second;
  76. Real expm1_meta = expm1(-eta);
  77. Real exp_meta = exp(-eta);
  78. Real node = -n*pi<Real>()/expm1_meta;
  79. // I have verified that this is not a significant source of inaccuracy in the weight computation:
  80. Real phi_prime = -(expm1_meta + x*exp_meta*eta_prime)/(expm1_meta*expm1_meta);
  81. // The main source of inaccuracy is in computation of sin_pi.
  82. // But I've agonized over this, and I think it's as good as it can get:
  83. Real s = pi<Real>();
  84. Real arg;
  85. if(eta > 1) {
  86. arg = n/( 1/exp_meta - 1 );
  87. s *= boost::math::sin_pi(arg);
  88. if (n&1) {
  89. s *= -1;
  90. }
  91. }
  92. else if (eta < -1) {
  93. arg = n/(1-exp_meta);
  94. s *= boost::math::sin_pi(arg);
  95. }
  96. else {
  97. arg = -n*exp_meta/expm1_meta;
  98. s *= boost::math::sin_pi(arg);
  99. if (n&1) {
  100. s *= -1;
  101. }
  102. }
  103. Real weight = s*phi_prime;
  104. return {node, weight};
  105. }
  106. #ifdef BOOST_MATH_INSTRUMENT_OOURA
  107. template<class Real>
  108. void print_ooura_estimate(size_t i, Real I0, Real I1, Real omega) {
  109. using std::abs;
  110. std::cout << std::defaultfloat
  111. << std::setprecision(std::numeric_limits<Real>::digits10)
  112. << std::fixed;
  113. std::cout << "h = " << Real(1)/Real(1<<i) << ", I_h = " << I0/omega
  114. << " = " << std::hexfloat << I0/omega << ", absolute error estimate = "
  115. << std::defaultfloat << std::scientific << abs(I0-I1) << std::endl;
  116. }
  117. #endif
  118. template<class Real>
  119. std::pair<Real, Real> ooura_cos_node_and_weight(long n, Real h, Real alpha)
  120. {
  121. using std::expm1;
  122. using std::exp;
  123. using std::abs;
  124. using boost::math::constants::pi;
  125. Real x = h*(n-Real(1)/Real(2));
  126. auto p = ooura_eta(x, alpha);
  127. auto eta = p.first;
  128. auto eta_prime = p.second;
  129. Real expm1_meta = expm1(-eta);
  130. Real exp_meta = exp(-eta);
  131. Real node = pi<Real>()*(Real(1)/Real(2)-n)/expm1_meta;
  132. Real phi_prime = -(expm1_meta + x*exp_meta*eta_prime)/(expm1_meta*expm1_meta);
  133. // Takuya Ooura and Masatake Mori,
  134. // Journal of Computational and Applied Mathematics, 112 (1999) 229-241.
  135. // A robust double exponential formula for Fourier-type integrals.
  136. // Equation 4.6
  137. Real s = pi<Real>();
  138. Real arg;
  139. if (eta < -1) {
  140. arg = -(n-Real(1)/Real(2))/expm1_meta;
  141. s *= boost::math::cos_pi(arg);
  142. }
  143. else {
  144. arg = -(n-Real(1)/Real(2))*exp_meta/expm1_meta;
  145. s *= boost::math::sin_pi(arg);
  146. if (n&1) {
  147. s *= -1;
  148. }
  149. }
  150. Real weight = s*phi_prime;
  151. return {node, weight};
  152. }
  153. template<class Real>
  154. class ooura_fourier_sin_detail {
  155. public:
  156. ooura_fourier_sin_detail(const Real relative_error_goal, size_t levels) {
  157. #ifdef BOOST_MATH_INSTRUMENT_OOURA
  158. std::cout << "ooura_fourier_sin with relative error goal " << relative_error_goal
  159. << " & " << levels << " levels." << std::endl;
  160. #endif // BOOST_MATH_INSTRUMENT_OOURA
  161. if (relative_error_goal < std::numeric_limits<Real>::epsilon() * 2) {
  162. throw std::domain_error("The relative error goal cannot be smaller than the unit roundoff.");
  163. }
  164. using std::abs;
  165. requested_levels_ = levels;
  166. starting_level_ = 0;
  167. rel_err_goal_ = relative_error_goal;
  168. big_nodes_.reserve(levels);
  169. bweights_.reserve(levels);
  170. little_nodes_.reserve(levels);
  171. lweights_.reserve(levels);
  172. for (size_t i = 0; i < levels; ++i) {
  173. if (std::is_same<Real, float>::value) {
  174. add_level<double>(i);
  175. }
  176. else if (std::is_same<Real, double>::value) {
  177. add_level<long double>(i);
  178. }
  179. else {
  180. add_level<Real>(i);
  181. }
  182. }
  183. }
  184. std::vector<std::vector<Real>> const & big_nodes() const {
  185. return big_nodes_;
  186. }
  187. std::vector<std::vector<Real>> const & weights_for_big_nodes() const {
  188. return bweights_;
  189. }
  190. std::vector<std::vector<Real>> const & little_nodes() const {
  191. return little_nodes_;
  192. }
  193. std::vector<std::vector<Real>> const & weights_for_little_nodes() const {
  194. return lweights_;
  195. }
  196. template<class F>
  197. std::pair<Real,Real> integrate(F const & f, Real omega) {
  198. using std::abs;
  199. using std::max;
  200. using boost::math::constants::pi;
  201. if (omega == 0) {
  202. return {Real(0), Real(0)};
  203. }
  204. if (omega < 0) {
  205. auto p = this->integrate(f, -omega);
  206. return {-p.first, p.second};
  207. }
  208. Real I1 = std::numeric_limits<Real>::quiet_NaN();
  209. Real relative_error_estimate = std::numeric_limits<Real>::quiet_NaN();
  210. // As we compute integrals, we learn about their structure.
  211. // Assuming we compute f(t)sin(wt) for many different omega, this gives some
  212. // a posteriori ability to choose a refinement level that is roughly appropriate.
  213. size_t i = starting_level_;
  214. do {
  215. Real I0 = estimate_integral(f, omega, i);
  216. #ifdef BOOST_MATH_INSTRUMENT_OOURA
  217. print_ooura_estimate(i, I0, I1, omega);
  218. #endif
  219. Real absolute_error_estimate = abs(I0-I1);
  220. Real scale = (max)(abs(I0), abs(I1));
  221. if (!isnan(I1) && absolute_error_estimate <= rel_err_goal_*scale) {
  222. starting_level_ = (max)(long(i) - 1, long(0));
  223. return {I0/omega, absolute_error_estimate/scale};
  224. }
  225. I1 = I0;
  226. } while(++i < big_nodes_.size());
  227. // We've used up all our precomputed levels.
  228. // Now we need to add more.
  229. // It might seems reasonable to just keep adding levels indefinitely, if that's what the user wants.
  230. // But in fact the nodes and weights just merge into each other and the error gets worse after a certain number.
  231. // This value for max_additional_levels was chosen by observation of a slowly converging oscillatory integral:
  232. // f(x) := cos(7cos(x))sin(x)/x
  233. size_t max_additional_levels = 4;
  234. while (big_nodes_.size() < requested_levels_ + max_additional_levels) {
  235. size_t ii = big_nodes_.size();
  236. if (std::is_same<Real, float>::value) {
  237. add_level<double>(ii);
  238. }
  239. else if (std::is_same<Real, double>::value) {
  240. add_level<long double>(ii);
  241. }
  242. else {
  243. add_level<Real>(ii);
  244. }
  245. Real I0 = estimate_integral(f, omega, ii);
  246. Real absolute_error_estimate = abs(I0-I1);
  247. Real scale = (max)(abs(I0), abs(I1));
  248. #ifdef BOOST_MATH_INSTRUMENT_OOURA
  249. print_ooura_estimate(ii, I0, I1, omega);
  250. #endif
  251. if (absolute_error_estimate <= rel_err_goal_*scale) {
  252. starting_level_ = (max)(long(ii) - 1, long(0));
  253. return {I0/omega, absolute_error_estimate/scale};
  254. }
  255. I1 = I0;
  256. ++ii;
  257. }
  258. starting_level_ = static_cast<long>(big_nodes_.size() - 2);
  259. return {I1/omega, relative_error_estimate};
  260. }
  261. private:
  262. template<class PreciseReal>
  263. void add_level(size_t i) {
  264. using std::abs;
  265. size_t current_num_levels = big_nodes_.size();
  266. Real unit_roundoff = std::numeric_limits<Real>::epsilon()/2;
  267. // h0 = 1. Then all further levels have h_i = 1/2^i.
  268. // Since the nodes don't nest, we could conceivably divide h by (say) 1.5, or 3.
  269. // It's not clear how much benefit (or loss) would be obtained from this.
  270. PreciseReal h = PreciseReal(1)/PreciseReal(1<<i);
  271. std::vector<Real> bnode_row;
  272. std::vector<Real> bweight_row;
  273. // This is a pretty good estimate for how many elements will be placed in the vector:
  274. bnode_row.reserve((static_cast<size_t>(1)<<i)*sizeof(Real));
  275. bweight_row.reserve((static_cast<size_t>(1)<<i)*sizeof(Real));
  276. std::vector<Real> lnode_row;
  277. std::vector<Real> lweight_row;
  278. lnode_row.reserve((static_cast<size_t>(1)<<i)*sizeof(Real));
  279. lweight_row.reserve((static_cast<size_t>(1)<<i)*sizeof(Real));
  280. Real max_weight = 1;
  281. auto alpha = calculate_ooura_alpha(h);
  282. long n = 0;
  283. Real w;
  284. do {
  285. auto precise_nw = ooura_sin_node_and_weight(n, h, alpha);
  286. Real node = static_cast<Real>(precise_nw.first);
  287. Real weight = static_cast<Real>(precise_nw.second);
  288. w = weight;
  289. if (bnode_row.size() == bnode_row.capacity()) {
  290. bnode_row.reserve(2*bnode_row.size());
  291. bweight_row.reserve(2*bnode_row.size());
  292. }
  293. bnode_row.push_back(node);
  294. bweight_row.push_back(weight);
  295. if (abs(weight) > max_weight) {
  296. max_weight = abs(weight);
  297. }
  298. ++n;
  299. // f(t)->0 as t->infty, which is why the weights are computed up to the unit roundoff.
  300. } while(abs(w) > unit_roundoff*max_weight);
  301. // This class tends to consume a lot of memory; shrink the vectors back down to size:
  302. bnode_row.shrink_to_fit();
  303. bweight_row.shrink_to_fit();
  304. // Why we are splitting the nodes into regimes where t_n >> 1 and t_n << 1?
  305. // It will create the opportunity to sensibly truncate the quadrature sum to significant terms.
  306. n = -1;
  307. do {
  308. auto precise_nw = ooura_sin_node_and_weight(n, h, alpha);
  309. Real node = static_cast<Real>(precise_nw.first);
  310. if (node <= 0) {
  311. break;
  312. }
  313. Real weight = static_cast<Real>(precise_nw.second);
  314. w = weight;
  315. using std::isnan;
  316. if (isnan(node)) {
  317. // This occurs at n = -11 in quad precision:
  318. break;
  319. }
  320. if (lnode_row.size() > 0) {
  321. if (lnode_row[lnode_row.size()-1] == node) {
  322. // The nodes have fused into each other:
  323. break;
  324. }
  325. }
  326. if (lnode_row.size() == lnode_row.capacity()) {
  327. lnode_row.reserve(2*lnode_row.size());
  328. lweight_row.reserve(2*lnode_row.size());
  329. }
  330. lnode_row.push_back(node);
  331. lweight_row.push_back(weight);
  332. if (abs(weight) > max_weight) {
  333. max_weight = abs(weight);
  334. }
  335. --n;
  336. // f(t)->infty is possible as t->0, hence compute up to the min.
  337. } while(abs(w) > (std::numeric_limits<Real>::min)()*max_weight);
  338. lnode_row.shrink_to_fit();
  339. lweight_row.shrink_to_fit();
  340. // std::scoped_lock once C++17 is more common?
  341. std::lock_guard<std::mutex> lock(node_weight_mutex_);
  342. // Another thread might have already finished this calculation and appended it to the nodes/weights:
  343. if (current_num_levels == big_nodes_.size()) {
  344. big_nodes_.push_back(bnode_row);
  345. bweights_.push_back(bweight_row);
  346. little_nodes_.push_back(lnode_row);
  347. lweights_.push_back(lweight_row);
  348. }
  349. }
  350. template<class F>
  351. Real estimate_integral(F const & f, Real omega, size_t i) {
  352. // Because so few function evaluations are required to get high accuracy on the integrals in the tests,
  353. // Kahan summation doesn't really help.
  354. //auto cond = boost::math::tools::summation_condition_number<Real, true>(0);
  355. Real I0 = 0;
  356. auto const & b_nodes = big_nodes_[i];
  357. auto const & b_weights = bweights_[i];
  358. // Will benchmark if this is helpful:
  359. Real inv_omega = 1/omega;
  360. for(size_t j = 0 ; j < b_nodes.size(); ++j) {
  361. I0 += f(b_nodes[j]*inv_omega)*b_weights[j];
  362. }
  363. auto const & l_nodes = little_nodes_[i];
  364. auto const & l_weights = lweights_[i];
  365. // If f decays rapidly as |t|->infty, not all of these calls are necessary.
  366. for (size_t j = 0; j < l_nodes.size(); ++j) {
  367. I0 += f(l_nodes[j]*inv_omega)*l_weights[j];
  368. }
  369. return I0;
  370. }
  371. std::mutex node_weight_mutex_;
  372. // Nodes for n >= 0, giving t_n = pi*phi(nh)/h. Generally t_n >> 1.
  373. std::vector<std::vector<Real>> big_nodes_;
  374. // The term bweights_ will indicate that these are weights corresponding
  375. // to the big nodes:
  376. std::vector<std::vector<Real>> bweights_;
  377. // Nodes for n < 0: Generally t_n << 1, and an invariant is that t_n > 0.
  378. std::vector<std::vector<Real>> little_nodes_;
  379. std::vector<std::vector<Real>> lweights_;
  380. Real rel_err_goal_;
  381. std::atomic<long> starting_level_;
  382. size_t requested_levels_;
  383. };
  384. template<class Real>
  385. class ooura_fourier_cos_detail {
  386. public:
  387. ooura_fourier_cos_detail(const Real relative_error_goal, size_t levels) {
  388. #ifdef BOOST_MATH_INSTRUMENT_OOURA
  389. std::cout << "ooura_fourier_cos with relative error goal " << relative_error_goal
  390. << " & " << levels << " levels." << std::endl;
  391. std::cout << "epsilon for type = " << std::numeric_limits<Real>::epsilon() << std::endl;
  392. #endif // BOOST_MATH_INSTRUMENT_OOURA
  393. if (relative_error_goal < std::numeric_limits<Real>::epsilon() * 2) {
  394. throw std::domain_error("The relative error goal cannot be smaller than the unit roundoff!");
  395. }
  396. using std::abs;
  397. requested_levels_ = levels;
  398. starting_level_ = 0;
  399. rel_err_goal_ = relative_error_goal;
  400. big_nodes_.reserve(levels);
  401. bweights_.reserve(levels);
  402. little_nodes_.reserve(levels);
  403. lweights_.reserve(levels);
  404. for (size_t i = 0; i < levels; ++i) {
  405. if (std::is_same<Real, float>::value) {
  406. add_level<double>(i);
  407. }
  408. else if (std::is_same<Real, double>::value) {
  409. add_level<long double>(i);
  410. }
  411. else {
  412. add_level<Real>(i);
  413. }
  414. }
  415. }
  416. template<class F>
  417. std::pair<Real,Real> integrate(F const & f, Real omega) {
  418. using std::abs;
  419. using std::max;
  420. using boost::math::constants::pi;
  421. if (omega == 0) {
  422. throw std::domain_error("At omega = 0, the integral is not oscillatory. The user must choose an appropriate method for this case.\n");
  423. }
  424. if (omega < 0) {
  425. return this->integrate(f, -omega);
  426. }
  427. Real I1 = std::numeric_limits<Real>::quiet_NaN();
  428. Real absolute_error_estimate = std::numeric_limits<Real>::quiet_NaN();
  429. Real scale = std::numeric_limits<Real>::quiet_NaN();
  430. size_t i = starting_level_;
  431. do {
  432. Real I0 = estimate_integral(f, omega, i);
  433. #ifdef BOOST_MATH_INSTRUMENT_OOURA
  434. print_ooura_estimate(i, I0, I1, omega);
  435. #endif
  436. absolute_error_estimate = abs(I0-I1);
  437. scale = (max)(abs(I0), abs(I1));
  438. if (!isnan(I1) && absolute_error_estimate <= rel_err_goal_*scale) {
  439. starting_level_ = (max)(long(i) - 1, long(0));
  440. return {I0/omega, absolute_error_estimate/scale};
  441. }
  442. I1 = I0;
  443. } while(++i < big_nodes_.size());
  444. size_t max_additional_levels = 4;
  445. while (big_nodes_.size() < requested_levels_ + max_additional_levels) {
  446. size_t ii = big_nodes_.size();
  447. if (std::is_same<Real, float>::value) {
  448. add_level<double>(ii);
  449. }
  450. else if (std::is_same<Real, double>::value) {
  451. add_level<long double>(ii);
  452. }
  453. else {
  454. add_level<Real>(ii);
  455. }
  456. Real I0 = estimate_integral(f, omega, ii);
  457. #ifdef BOOST_MATH_INSTRUMENT_OOURA
  458. print_ooura_estimate(ii, I0, I1, omega);
  459. #endif
  460. absolute_error_estimate = abs(I0-I1);
  461. scale = (max)(abs(I0), abs(I1));
  462. if (absolute_error_estimate <= rel_err_goal_*scale) {
  463. starting_level_ = (max)(long(ii) - 1, long(0));
  464. return {I0/omega, absolute_error_estimate/scale};
  465. }
  466. I1 = I0;
  467. ++ii;
  468. }
  469. starting_level_ = static_cast<long>(big_nodes_.size() - 2);
  470. return {I1/omega, absolute_error_estimate/scale};
  471. }
  472. private:
  473. template<class PreciseReal>
  474. void add_level(size_t i) {
  475. using std::abs;
  476. size_t current_num_levels = big_nodes_.size();
  477. Real unit_roundoff = std::numeric_limits<Real>::epsilon()/2;
  478. PreciseReal h = PreciseReal(1)/PreciseReal(1<<i);
  479. std::vector<Real> bnode_row;
  480. std::vector<Real> bweight_row;
  481. bnode_row.reserve((static_cast<size_t>(1)<<i)*sizeof(Real));
  482. bweight_row.reserve((static_cast<size_t>(1)<<i)*sizeof(Real));
  483. std::vector<Real> lnode_row;
  484. std::vector<Real> lweight_row;
  485. lnode_row.reserve((static_cast<size_t>(1)<<i)*sizeof(Real));
  486. lweight_row.reserve((static_cast<size_t>(1)<<i)*sizeof(Real));
  487. Real max_weight = 1;
  488. auto alpha = calculate_ooura_alpha(h);
  489. long n = 0;
  490. Real w;
  491. do {
  492. auto precise_nw = ooura_cos_node_and_weight(n, h, alpha);
  493. Real node = static_cast<Real>(precise_nw.first);
  494. Real weight = static_cast<Real>(precise_nw.second);
  495. w = weight;
  496. if (bnode_row.size() == bnode_row.capacity()) {
  497. bnode_row.reserve(2*bnode_row.size());
  498. bweight_row.reserve(2*bnode_row.size());
  499. }
  500. bnode_row.push_back(node);
  501. bweight_row.push_back(weight);
  502. if (abs(weight) > max_weight) {
  503. max_weight = abs(weight);
  504. }
  505. ++n;
  506. // f(t)->0 as t->infty, which is why the weights are computed up to the unit roundoff.
  507. } while(abs(w) > unit_roundoff*max_weight);
  508. bnode_row.shrink_to_fit();
  509. bweight_row.shrink_to_fit();
  510. n = -1;
  511. do {
  512. auto precise_nw = ooura_cos_node_and_weight(n, h, alpha);
  513. Real node = static_cast<Real>(precise_nw.first);
  514. // The function cannot be singular at zero,
  515. // so zero is not a unreasonable node,
  516. // unlike in the case of the Fourier Sine.
  517. // Hence only break if the node is negative.
  518. if (node < 0) {
  519. break;
  520. }
  521. Real weight = static_cast<Real>(precise_nw.second);
  522. w = weight;
  523. if (lnode_row.size() > 0) {
  524. if (lnode_row.back() == node) {
  525. // The nodes have fused into each other:
  526. break;
  527. }
  528. }
  529. if (lnode_row.size() == lnode_row.capacity()) {
  530. lnode_row.reserve(2*lnode_row.size());
  531. lweight_row.reserve(2*lnode_row.size());
  532. }
  533. lnode_row.push_back(node);
  534. lweight_row.push_back(weight);
  535. if (abs(weight) > max_weight) {
  536. max_weight = abs(weight);
  537. }
  538. --n;
  539. } while(abs(w) > (std::numeric_limits<Real>::min)()*max_weight);
  540. lnode_row.shrink_to_fit();
  541. lweight_row.shrink_to_fit();
  542. std::lock_guard<std::mutex> lock(node_weight_mutex_);
  543. // Another thread might have already finished this calculation and appended it to the nodes/weights:
  544. if (current_num_levels == big_nodes_.size()) {
  545. big_nodes_.push_back(bnode_row);
  546. bweights_.push_back(bweight_row);
  547. little_nodes_.push_back(lnode_row);
  548. lweights_.push_back(lweight_row);
  549. }
  550. }
  551. template<class F>
  552. Real estimate_integral(F const & f, Real omega, size_t i) {
  553. Real I0 = 0;
  554. auto const & b_nodes = big_nodes_[i];
  555. auto const & b_weights = bweights_[i];
  556. Real inv_omega = 1/omega;
  557. for(size_t j = 0 ; j < b_nodes.size(); ++j) {
  558. I0 += f(b_nodes[j]*inv_omega)*b_weights[j];
  559. }
  560. auto const & l_nodes = little_nodes_[i];
  561. auto const & l_weights = lweights_[i];
  562. for (size_t j = 0; j < l_nodes.size(); ++j) {
  563. I0 += f(l_nodes[j]*inv_omega)*l_weights[j];
  564. }
  565. return I0;
  566. }
  567. std::mutex node_weight_mutex_;
  568. std::vector<std::vector<Real>> big_nodes_;
  569. std::vector<std::vector<Real>> bweights_;
  570. std::vector<std::vector<Real>> little_nodes_;
  571. std::vector<std::vector<Real>> lweights_;
  572. Real rel_err_goal_;
  573. std::atomic<long> starting_level_;
  574. size_t requested_levels_;
  575. };
  576. }}}}
  577. #endif