chebyshev.hpp 9.6 KB

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