• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
7 /*
8  * This class performs tanh-sinh quadrature on the real line.
9  * Tanh-sinh quadrature is exponentially convergent for integrands in Hardy spaces,
10  * (see https://en.wikipedia.org/wiki/Hardy_space for a formal definition), and is optimal for a random function from that class.
11  *
12  * The tanh-sinh quadrature is one of a class of so called "double exponential quadratures"-there is a large family of them,
13  * but this one seems to be the most commonly used.
14  *
15  * As always, there are caveats: For instance, if the function you want to integrate is not holomorphic on the unit disk,
16  * then the rapid convergence will be spoiled. In this case, a more appropriate quadrature is (say) Romberg, which does not
17  * require the function to be holomorphic, only differentiable up to some order.
18  *
19  * In addition, if you are integrating a periodic function over a period, the trapezoidal rule is better.
20  *
21  * References:
22  *
23  * 1) Mori, Masatake. "Quadrature formulas obtained by variable transformation and the DE-rule." Journal of Computational and Applied Mathematics 12 (1985): 119-130.
24  * 2) Bailey, David H., Karthik Jeyabalan, and Xiaoye S. Li. "A comparison of three high-precision quadrature schemes." Experimental Mathematics 14.3 (2005): 317-329.
25  * 3) Press, William H., et al. "Numerical recipes third edition: the art of scientific computing." Cambridge University Press 32 (2007): 10013-2473.
26  *
27  */
28 
29 #ifndef BOOST_MATH_QUADRATURE_TANH_SINH_HPP
30 #define BOOST_MATH_QUADRATURE_TANH_SINH_HPP
31 
32 #include <cmath>
33 #include <limits>
34 #include <memory>
35 #include <boost/math/quadrature/detail/tanh_sinh_detail.hpp>
36 
37 namespace boost{ namespace math{ namespace quadrature {
38 
39 template<class Real, class Policy = policies::policy<> >
40 class tanh_sinh
41 {
42 public:
tanh_sinh(size_t max_refinements=15,const Real & min_complement=tools::min_value<Real> ()* 4)43     tanh_sinh(size_t max_refinements = 15, const Real& min_complement = tools::min_value<Real>() * 4)
44     : m_imp(std::make_shared<detail::tanh_sinh_detail<Real, Policy>>(max_refinements, min_complement)) {}
45 
46     template<class F>
47     auto integrate(const F f, Real a, Real b, Real tolerance = tools::root_epsilon<Real>(), Real* error = nullptr, Real* L1 = nullptr, std::size_t* levels = nullptr) ->decltype(std::declval<F>()(std::declval<Real>())) const;
48     template<class F>
49     auto integrate(const F f, Real a, Real b, Real tolerance = tools::root_epsilon<Real>(), Real* error = nullptr, Real* L1 = nullptr, std::size_t* levels = nullptr) ->decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) const;
50 
51     template<class F>
52     auto integrate(const F f, Real tolerance = tools::root_epsilon<Real>(), Real* error = nullptr, Real* L1 = nullptr, std::size_t* levels = nullptr) ->decltype(std::declval<F>()(std::declval<Real>())) const;
53     template<class F>
54     auto integrate(const F f, Real tolerance = tools::root_epsilon<Real>(), Real* error = nullptr, Real* L1 = nullptr, std::size_t* levels = nullptr) ->decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) const;
55 
56 private:
57     std::shared_ptr<detail::tanh_sinh_detail<Real, Policy>> m_imp;
58 };
59 
60 template<class Real, class Policy>
61 template<class F>
integrate(const F f,Real a,Real b,Real tolerance,Real * error,Real * L1,std::size_t * levels)62 auto tanh_sinh<Real, Policy>::integrate(const F f, Real a, Real b, Real tolerance, Real* error, Real* L1, std::size_t* levels) ->decltype(std::declval<F>()(std::declval<Real>())) const
63 {
64     BOOST_MATH_STD_USING
65     using boost::math::constants::half;
66     using boost::math::quadrature::detail::tanh_sinh_detail;
67 
68     static const char* function = "tanh_sinh<%1%>::integrate";
69 
70     typedef decltype(std::declval<F>()(std::declval<Real>())) result_type;
71 
72     if (!(boost::math::isnan)(a) && !(boost::math::isnan)(b))
73     {
74 
75        // Infinite limits:
76        if ((a <= -tools::max_value<Real>()) && (b >= tools::max_value<Real>()))
77        {
78           auto u = [&](const Real& t, const Real& tc)->result_type
79           {
80              Real t_sq = t*t;
81              Real inv;
82              if (t > 0.5f)
83                 inv = 1 / ((2 - tc) * tc);
84              else if(t < -0.5)
85                 inv = 1 / ((2 + tc) * -tc);
86              else
87                 inv = 1 / (1 - t_sq);
88              return f(t*inv)*(1 + t_sq)*inv*inv;
89           };
90           Real limit = sqrt(tools::min_value<Real>()) * 4;
91           return m_imp->integrate(u, error, L1, function, limit, limit, tolerance, levels);
92        }
93 
94        // Right limit is infinite:
95        if ((boost::math::isfinite)(a) && (b >= tools::max_value<Real>()))
96        {
97           auto u = [&](const Real& t, const Real& tc)->result_type
98           {
99              Real z, arg;
100              if (t > -0.5f)
101                 z = 1 / (t + 1);
102              else
103                 z = -1 / tc;
104              if (t < 0.5)
105                 arg = 2 * z + a - 1;
106              else
107                 arg = a + tc / (2 - tc);
108              return f(arg)*z*z;
109           };
110           Real left_limit = sqrt(tools::min_value<Real>()) * 4;
111           result_type Q = Real(2) * m_imp->integrate(u, error, L1, function, left_limit, tools::min_value<Real>(), tolerance, levels);
112           if (L1)
113           {
114              *L1 *= 2;
115           }
116 
117           return Q;
118        }
119 
120        if ((boost::math::isfinite)(b) && (a <= -tools::max_value<Real>()))
121        {
122           auto v = [&](const Real& t, const Real& tc)->result_type
123           {
124              Real z;
125              if (t > -0.5)
126                 z = 1 / (t + 1);
127              else
128                 z = -1 / tc;
129              Real arg;
130              if (t < 0.5)
131                 arg = 2 * z - 1;
132              else
133                 arg = tc / (2 - tc);
134              return f(b - arg) * z * z;
135           };
136 
137           Real left_limit = sqrt(tools::min_value<Real>()) * 4;
138           result_type Q = Real(2) * m_imp->integrate(v, error, L1, function, left_limit, tools::min_value<Real>(), tolerance, levels);
139           if (L1)
140           {
141              *L1 *= 2;
142           }
143           return Q;
144        }
145 
146        if ((boost::math::isfinite)(a) && (boost::math::isfinite)(b))
147        {
148           if (a == b)
149           {
150              return result_type(0);
151           }
152           if (b < a)
153           {
154              return -this->integrate(f, b, a, tolerance, error, L1, levels);
155           }
156           Real avg = (a + b)*half<Real>();
157           Real diff = (b - a)*half<Real>();
158           Real avg_over_diff_m1 = a / diff;
159           Real avg_over_diff_p1 = b / diff;
160           bool have_small_left = fabs(a) < 0.5f;
161           bool have_small_right = fabs(b) < 0.5f;
162           Real left_min_complement = float_next(avg_over_diff_m1) - avg_over_diff_m1;
163           Real min_complement_limit = (std::max)(tools::min_value<Real>(), Real(tools::min_value<Real>() / diff));
164           if (left_min_complement < min_complement_limit)
165              left_min_complement = min_complement_limit;
166           Real right_min_complement = avg_over_diff_p1 - float_prior(avg_over_diff_p1);
167           if (right_min_complement < min_complement_limit)
168              right_min_complement = min_complement_limit;
169           //
170           // These asserts will fail only if rounding errors on
171           // type Real have accumulated so much error that it's
172           // broken our internal logic.  Should that prove to be
173           // a persistent issue, we might need to add a bit of fudge
174           // factor to move left_min_complement and right_min_complement
175           // further from the end points of the range.
176           //
177           BOOST_ASSERT((left_min_complement * diff + a) > a);
178           BOOST_ASSERT((b - right_min_complement * diff) < b);
179           auto u = [&](Real z, Real zc)->result_type
180           {
181              Real position;
182              if (z < -0.5)
183              {
184                 if(have_small_left)
185                   return f(diff * (avg_over_diff_m1 - zc));
186                 position = a - diff * zc;
187              }
188              if (z > 0.5)
189              {
190                 if(have_small_right)
191                   return f(diff * (avg_over_diff_p1 - zc));
192                 position = b - diff * zc;
193              }
194              else
195                 position = avg + diff*z;
196              BOOST_ASSERT(position != a);
197              BOOST_ASSERT(position != b);
198              return f(position);
199           };
200           result_type Q = diff*m_imp->integrate(u, error, L1, function, left_min_complement, right_min_complement, tolerance, levels);
201 
202           if (L1)
203           {
204              *L1 *= diff;
205           }
206           return Q;
207        }
208     }
209     return policies::raise_domain_error(function, "The domain of integration is not sensible; please check the bounds.", a, Policy());
210 }
211 
212 template<class Real, class Policy>
213 template<class F>
integrate(const F f,Real a,Real b,Real tolerance,Real * error,Real * L1,std::size_t * levels)214 auto tanh_sinh<Real, Policy>::integrate(const F f, Real a, Real b, Real tolerance, Real* error, Real* L1, std::size_t* levels) ->decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) const
215 {
216    BOOST_MATH_STD_USING
217       using boost::math::constants::half;
218    using boost::math::quadrature::detail::tanh_sinh_detail;
219 
220    static const char* function = "tanh_sinh<%1%>::integrate";
221 
222    if ((boost::math::isfinite)(a) && (boost::math::isfinite)(b))
223    {
224       if (b <= a)
225       {
226          return policies::raise_domain_error(function, "Arguments to integrate are in wrong order; integration over [a,b] must have b > a.", a, Policy());
227       }
228       auto u = [&](Real z, Real zc)->Real
229       {
230          if (z < 0)
231             return f((a - b) * zc / 2 + a, (b - a) * zc / 2);
232          else
233             return f((a - b) * zc / 2 + b, (b - a) * zc / 2);
234       };
235       Real diff = (b - a)*half<Real>();
236       Real left_min_complement = tools::min_value<Real>() * 4;
237       Real right_min_complement = tools::min_value<Real>() * 4;
238       Real Q = diff*m_imp->integrate(u, error, L1, function, left_min_complement, right_min_complement, tolerance, levels);
239 
240       if (L1)
241       {
242          *L1 *= diff;
243       }
244       return Q;
245    }
246    return policies::raise_domain_error(function, "The domain of integration is not sensible; please check the bounds.", a, Policy());
247 }
248 
249 template<class Real, class Policy>
250 template<class F>
integrate(const F f,Real tolerance,Real * error,Real * L1,std::size_t * levels)251 auto tanh_sinh<Real, Policy>::integrate(const F f, Real tolerance, Real* error, Real* L1, std::size_t* levels) ->decltype(std::declval<F>()(std::declval<Real>())) const
252 {
253    using boost::math::quadrature::detail::tanh_sinh_detail;
254    static const char* function = "tanh_sinh<%1%>::integrate";
255    Real min_complement = tools::epsilon<Real>();
256    return m_imp->integrate([&](const Real& arg, const Real&) { return f(arg); }, error, L1, function, min_complement, min_complement, tolerance, levels);
257 }
258 
259 template<class Real, class Policy>
260 template<class F>
integrate(const F f,Real tolerance,Real * error,Real * L1,std::size_t * levels)261 auto tanh_sinh<Real, Policy>::integrate(const F f, Real tolerance, Real* error, Real* L1, std::size_t* levels) ->decltype(std::declval<F>()(std::declval<Real>(), std::declval<Real>())) const
262 {
263    using boost::math::quadrature::detail::tanh_sinh_detail;
264    static const char* function = "tanh_sinh<%1%>::integrate";
265    Real min_complement = tools::min_value<Real>() * 4;
266    return m_imp->integrate(f, error, L1, function, min_complement, min_complement, tolerance, levels);
267 }
268 
269 }
270 }
271 }
272 #endif
273