chebyshev.hpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // (C) Copyright Nick Thompson 2017.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_SPECIAL_CHEBYSHEV_HPP
  6. #define BOOST_MATH_SPECIAL_CHEBYSHEV_HPP
  7. #include <cmath>
  8. #include <boost/math/special_functions/math_fwd.hpp>
  9. #include <boost/math/policies/error_handling.hpp>
  10. #include <boost/math/constants/constants.hpp>
  11. #include <boost/math/tools/promotion.hpp>
  12. #include <boost/math/tools/throw_exception.hpp>
  13. #if (__cplusplus > 201103) || (defined(_CPPLIB_VER) && (_CPPLIB_VER >= 610))
  14. # define BOOST_MATH_CHEB_USE_STD_ACOSH
  15. #endif
  16. #ifndef BOOST_MATH_CHEB_USE_STD_ACOSH
  17. # include <boost/math/special_functions/acosh.hpp>
  18. #endif
  19. namespace boost { namespace math {
  20. template <class T1, class T2, class T3>
  21. inline tools::promote_args_t<T1, T2, T3> chebyshev_next(T1 const & x, T2 const & Tn, T3 const & Tn_1)
  22. {
  23. return 2*x*Tn - Tn_1;
  24. }
  25. namespace detail {
  26. template<class Real, bool second, class Policy>
  27. inline Real chebyshev_imp(unsigned n, Real const & x, const Policy&)
  28. {
  29. #ifdef BOOST_MATH_CHEB_USE_STD_ACOSH
  30. using std::acosh;
  31. #define BOOST_MATH_ACOSH_POLICY
  32. #else
  33. using boost::math::acosh;
  34. #define BOOST_MATH_ACOSH_POLICY , Policy()
  35. #endif
  36. using std::cosh;
  37. using std::pow;
  38. using std::sqrt;
  39. Real T0 = 1;
  40. Real T1;
  41. BOOST_IF_CONSTEXPR (second)
  42. {
  43. if (x > 1 || x < -1)
  44. {
  45. Real t = sqrt(x*x -1);
  46. return static_cast<Real>((pow(x+t, static_cast<int>(n+1)) - pow(x-t, static_cast<int>(n+1)))/(2*t));
  47. }
  48. T1 = 2*x;
  49. }
  50. else
  51. {
  52. if (x > 1)
  53. {
  54. return cosh(n*acosh(x BOOST_MATH_ACOSH_POLICY));
  55. }
  56. if (x < -1)
  57. {
  58. if (n & 1)
  59. {
  60. return -cosh(n*acosh(-x BOOST_MATH_ACOSH_POLICY));
  61. }
  62. else
  63. {
  64. return cosh(n*acosh(-x BOOST_MATH_ACOSH_POLICY));
  65. }
  66. }
  67. T1 = x;
  68. }
  69. if (n == 0)
  70. {
  71. return T0;
  72. }
  73. unsigned l = 1;
  74. while(l < n)
  75. {
  76. std::swap(T0, T1);
  77. T1 = boost::math::chebyshev_next(x, T0, T1);
  78. ++l;
  79. }
  80. return T1;
  81. }
  82. } // namespace detail
  83. template <class Real, class Policy>
  84. inline tools::promote_args_t<Real> chebyshev_t(unsigned n, Real const & x, const Policy&)
  85. {
  86. using result_type = tools::promote_args_t<Real>;
  87. using value_type = typename policies::evaluation<result_type, Policy>::type;
  88. using forwarding_policy = typename policies::normalise<
  89. Policy,
  90. policies::promote_float<false>,
  91. policies::promote_double<false>,
  92. policies::discrete_quantile<>,
  93. policies::assert_undefined<> >::type;
  94. return policies::checked_narrowing_cast<result_type, Policy>(detail::chebyshev_imp<value_type, false>(n, static_cast<value_type>(x), forwarding_policy()), "boost::math::chebyshev_t<%1%>(unsigned, %1%)");
  95. }
  96. template <class Real>
  97. inline tools::promote_args_t<Real> chebyshev_t(unsigned n, Real const & x)
  98. {
  99. return chebyshev_t(n, x, policies::policy<>());
  100. }
  101. template <class Real, class Policy>
  102. inline tools::promote_args_t<Real> chebyshev_u(unsigned n, Real const & x, const Policy&)
  103. {
  104. using result_type = tools::promote_args_t<Real>;
  105. using value_type = typename policies::evaluation<result_type, Policy>::type;
  106. using forwarding_policy = typename policies::normalise<
  107. Policy,
  108. policies::promote_float<false>,
  109. policies::promote_double<false>,
  110. policies::discrete_quantile<>,
  111. policies::assert_undefined<> >::type;
  112. return policies::checked_narrowing_cast<result_type, Policy>(detail::chebyshev_imp<value_type, true>(n, static_cast<value_type>(x), forwarding_policy()), "boost::math::chebyshev_u<%1%>(unsigned, %1%)");
  113. }
  114. template <class Real>
  115. inline tools::promote_args_t<Real> chebyshev_u(unsigned n, Real const & x)
  116. {
  117. return chebyshev_u(n, x, policies::policy<>());
  118. }
  119. template <class Real, class Policy>
  120. inline tools::promote_args_t<Real> chebyshev_t_prime(unsigned n, Real const & x, const Policy&)
  121. {
  122. using result_type = tools::promote_args_t<Real>;
  123. using value_type = typename policies::evaluation<result_type, Policy>::type;
  124. using forwarding_policy = typename policies::normalise<
  125. Policy,
  126. policies::promote_float<false>,
  127. policies::promote_double<false>,
  128. policies::discrete_quantile<>,
  129. policies::assert_undefined<> >::type;
  130. if (n == 0)
  131. {
  132. return result_type(0);
  133. }
  134. return policies::checked_narrowing_cast<result_type, Policy>(n * detail::chebyshev_imp<value_type, true>(n - 1, static_cast<value_type>(x), forwarding_policy()), "boost::math::chebyshev_t_prime<%1%>(unsigned, %1%)");
  135. }
  136. template <class Real>
  137. inline tools::promote_args_t<Real> chebyshev_t_prime(unsigned n, Real const & x)
  138. {
  139. return chebyshev_t_prime(n, x, policies::policy<>());
  140. }
  141. /*
  142. * This is Algorithm 3.1 of
  143. * Gil, Amparo, Javier Segura, and Nico M. Temme.
  144. * Numerical methods for special functions.
  145. * Society for Industrial and Applied Mathematics, 2007.
  146. * https://www.siam.org/books/ot99/OT99SampleChapter.pdf
  147. * However, our definition of c0 differs by a factor of 1/2, as stated in the docs. . .
  148. */
  149. template <class Real, class T2>
  150. inline Real chebyshev_clenshaw_recurrence(const Real* const c, size_t length, const T2& x)
  151. {
  152. using boost::math::constants::half;
  153. if (length < 2)
  154. {
  155. if (length == 0)
  156. {
  157. return 0;
  158. }
  159. return c[0]/2;
  160. }
  161. Real b2 = 0;
  162. Real b1 = c[length -1];
  163. for(size_t j = length - 2; j >= 1; --j)
  164. {
  165. Real tmp = 2*x*b1 - b2 + c[j];
  166. b2 = b1;
  167. b1 = tmp;
  168. }
  169. return x*b1 - b2 + half<Real>()*c[0];
  170. }
  171. namespace detail {
  172. template <class Real>
  173. inline Real unchecked_chebyshev_clenshaw_recurrence(const Real* const c, size_t length, const Real & a, const Real & b, const Real& x)
  174. {
  175. Real t;
  176. Real u;
  177. // This cutoff is not super well defined, but it's a good estimate.
  178. // See "An Error Analysis of the Modified Clenshaw Method for Evaluating Chebyshev and Fourier Series"
  179. // J. OLIVER, IMA Journal of Applied Mathematics, Volume 20, Issue 3, November 1977, Pages 379-391
  180. // https://doi.org/10.1093/imamat/20.3.379
  181. const auto cutoff = static_cast<Real>(0.6L);
  182. if (x - a < b - x)
  183. {
  184. u = 2*(x-a)/(b-a);
  185. t = u - 1;
  186. if (t > -cutoff)
  187. {
  188. Real b2 = 0;
  189. Real b1 = c[length -1];
  190. for(size_t j = length - 2; j >= 1; --j)
  191. {
  192. Real tmp = 2*t*b1 - b2 + c[j];
  193. b2 = b1;
  194. b1 = tmp;
  195. }
  196. return t*b1 - b2 + c[0]/2;
  197. }
  198. else
  199. {
  200. Real b1 = c[length - 1];
  201. Real d = b1;
  202. Real b2 = 0;
  203. for (size_t r = length - 2; r >= 1; --r)
  204. {
  205. d = 2*u*b1 - d + c[r];
  206. b2 = b1;
  207. b1 = d - b1;
  208. }
  209. return t*b1 - b2 + c[0]/2;
  210. }
  211. }
  212. else
  213. {
  214. u = -2*(b-x)/(b-a);
  215. t = u + 1;
  216. if (t < cutoff)
  217. {
  218. Real b2 = 0;
  219. Real b1 = c[length -1];
  220. for(size_t j = length - 2; j >= 1; --j)
  221. {
  222. Real tmp = 2*t*b1 - b2 + c[j];
  223. b2 = b1;
  224. b1 = tmp;
  225. }
  226. return t*b1 - b2 + c[0]/2;
  227. }
  228. else
  229. {
  230. Real b1 = c[length - 1];
  231. Real d = b1;
  232. Real b2 = 0;
  233. for (size_t r = length - 2; r >= 1; --r)
  234. {
  235. d = 2*u*b1 + d + c[r];
  236. b2 = b1;
  237. b1 = d + b1;
  238. }
  239. return t*b1 - b2 + c[0]/2;
  240. }
  241. }
  242. }
  243. } // namespace detail
  244. template <class Real>
  245. inline Real chebyshev_clenshaw_recurrence(const Real* const c, size_t length, const Real & a, const Real & b, const Real& x)
  246. {
  247. if (x < a || x > b)
  248. {
  249. BOOST_MATH_THROW_EXCEPTION(std::domain_error("x in [a, b] is required."));
  250. }
  251. if (length < 2)
  252. {
  253. if (length == 0)
  254. {
  255. return 0;
  256. }
  257. return c[0]/2;
  258. }
  259. return detail::unchecked_chebyshev_clenshaw_recurrence(c, length, a, b, x);
  260. }
  261. }} // Namespace boost::math
  262. #endif // BOOST_MATH_SPECIAL_CHEBYSHEV_HPP