trapezoidal.hpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  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. * Use the adaptive trapezoidal rule to estimate the integral of periodic functions over a period,
  8. * or to integrate a function whose derivative vanishes at the endpoints.
  9. *
  10. * If your function does not satisfy these conditions, and instead is simply continuous and bounded
  11. * over the whole interval, then this routine will still converge, albeit slowly. However, there
  12. * are much more efficient methods in this case, including Romberg, Simpson, and double exponential quadrature.
  13. */
  14. #ifndef BOOST_MATH_QUADRATURE_TRAPEZOIDAL_HPP
  15. #define BOOST_MATH_QUADRATURE_TRAPEZOIDAL_HPP
  16. #include <cmath>
  17. #include <limits>
  18. #include <stdexcept>
  19. #include <boost/math/constants/constants.hpp>
  20. #include <boost/math/special_functions/fpclassify.hpp>
  21. #include <boost/math/policies/error_handling.hpp>
  22. namespace boost{ namespace math{ namespace quadrature {
  23. template<class F, class Real, class Policy>
  24. auto trapezoidal(F f, Real a, Real b, Real tol, std::size_t max_refinements, Real* error_estimate, Real* L1, const Policy& pol)->decltype(std::declval<F>()(std::declval<Real>()))
  25. {
  26. static const char* function = "boost::math::quadrature::trapezoidal<%1%>(F, %1%, %1%, %1%)";
  27. using std::abs;
  28. using boost::math::constants::half;
  29. // In many math texts, K represents the field of real or complex numbers.
  30. // Too bad we can't put blackboard bold into C++ source!
  31. typedef decltype(f(a)) K;
  32. if(a >= b)
  33. {
  34. return static_cast<K>(boost::math::policies::raise_domain_error(function, "a < b for integration over the region [a, b] is required, but got a = %1%.\n", a, pol));
  35. }
  36. if (!(boost::math::isfinite)(a))
  37. {
  38. return static_cast<K>(boost::math::policies::raise_domain_error(function, "Left endpoint of integration must be finite for adaptive trapezoidal integration but got a = %1%.\n", a, pol));
  39. }
  40. if (!(boost::math::isfinite)(b))
  41. {
  42. return static_cast<K>(boost::math::policies::raise_domain_error(function, "Right endpoint of integration must be finite for adaptive trapedzoidal integration but got b = %1%.\n", b, pol));
  43. }
  44. K ya = f(a);
  45. K yb = f(b);
  46. Real h = (b - a)*half<Real>();
  47. K I0 = (ya + yb)*h;
  48. Real IL0 = (abs(ya) + abs(yb))*h;
  49. K yh = f(a + h);
  50. K I1;
  51. I1 = I0*half<Real>() + yh*h;
  52. Real IL1 = IL0*half<Real>() + abs(yh)*h;
  53. // The recursion is:
  54. // I_k = 1/2 I_{k-1} + 1/2^k \sum_{j=1; j odd, j < 2^k} f(a + j(b-a)/2^k)
  55. std::size_t k = 2;
  56. // We want to go through at least 4 levels so we have sampled the function at least 10 times.
  57. // Otherwise, we could terminate prematurely and miss essential features.
  58. // This is of course possible anyway, but 10 samples seems to be a reasonable compromise.
  59. Real error = abs(I0 - I1);
  60. while (k < 4 || (k < max_refinements && error > tol*IL1) )
  61. {
  62. I0 = I1;
  63. IL0 = IL1;
  64. I1 = I0*half<Real>();
  65. IL1 = IL0*half<Real>();
  66. std::size_t p = static_cast<std::size_t>(1u) << k;
  67. h *= half<Real>();
  68. K sum = 0;
  69. Real absum = 0;
  70. for(std::size_t j = 1; j < p; j += 2)
  71. {
  72. K y = f(a + j*h);
  73. sum += y;
  74. absum += abs(y);
  75. }
  76. I1 += sum*h;
  77. IL1 += absum*h;
  78. ++k;
  79. error = abs(I0 - I1);
  80. }
  81. if (error_estimate)
  82. {
  83. *error_estimate = error;
  84. }
  85. if (L1)
  86. {
  87. *L1 = IL1;
  88. }
  89. return static_cast<K>(I1);
  90. }
  91. #if BOOST_WORKAROUND(BOOST_MSVC, < 1800)
  92. // Template argument dedcution failure otherwise:
  93. template<class F, class Real>
  94. auto trapezoidal(F f, Real a, Real b, Real tol = 0, std::size_t max_refinements = 12, Real* error_estimate = 0, Real* L1 = 0)->decltype(std::declval<F>()(std::declval<Real>()))
  95. #elif !defined(BOOST_NO_CXX11_NULLPTR)
  96. template<class F, class Real>
  97. auto trapezoidal(F f, Real a, Real b, Real tol = boost::math::tools::root_epsilon<Real>(), std::size_t max_refinements = 12, Real* error_estimate = nullptr, Real* L1 = nullptr)->decltype(std::declval<F>()(std::declval<Real>()))
  98. #else
  99. template<class F, class Real>
  100. auto trapezoidal(F f, Real a, Real b, Real tol = boost::math::tools::root_epsilon<Real>(), std::size_t max_refinements = 12, Real* error_estimate = 0, Real* L1 = 0)->decltype(std::declval<F>()(std::declval<Real>()))
  101. #endif
  102. {
  103. #if BOOST_WORKAROUND(BOOST_MSVC, <= 1600)
  104. if (tol == 0)
  105. tol = boost::math::tools::root_epsilon<Real>();
  106. #endif
  107. return trapezoidal(f, a, b, tol, max_refinements, error_estimate, L1, boost::math::policies::policy<>());
  108. }
  109. }}}
  110. #endif