students_t.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. // Copyright John Maddock 2006.
  2. // Copyright Paul A. Bristow 2006, 2012, 2017.
  3. // Copyright Thomas Mang 2012.
  4. // Use, modification and distribution are subject to the
  5. // Boost Software License, Version 1.0. (See accompanying file
  6. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. #ifndef BOOST_STATS_STUDENTS_T_HPP
  8. #define BOOST_STATS_STUDENTS_T_HPP
  9. // http://en.wikipedia.org/wiki/Student%27s_t_distribution
  10. // http://www.itl.nist.gov/div898/handbook/eda/section3/eda3664.htm
  11. #include <boost/math/distributions/fwd.hpp>
  12. #include <boost/math/special_functions/beta.hpp> // for ibeta(a, b, x).
  13. #include <boost/math/distributions/complement.hpp>
  14. #include <boost/math/distributions/detail/common_error_handling.hpp>
  15. #include <boost/math/distributions/normal.hpp>
  16. #include <utility>
  17. #ifdef BOOST_MSVC
  18. # pragma warning(push)
  19. # pragma warning(disable: 4702) // unreachable code (return after domain_error throw).
  20. #endif
  21. namespace boost { namespace math {
  22. template <class RealType = double, class Policy = policies::policy<> >
  23. class students_t_distribution
  24. {
  25. public:
  26. typedef RealType value_type;
  27. typedef Policy policy_type;
  28. students_t_distribution(RealType df) : df_(df)
  29. { // Constructor.
  30. RealType result;
  31. detail::check_df_gt0_to_inf( // Checks that df > 0 or df == inf.
  32. "boost::math::students_t_distribution<%1%>::students_t_distribution", df_, &result, Policy());
  33. } // students_t_distribution
  34. RealType degrees_of_freedom()const
  35. {
  36. return df_;
  37. }
  38. // Parameter estimation:
  39. static RealType find_degrees_of_freedom(
  40. RealType difference_from_mean,
  41. RealType alpha,
  42. RealType beta,
  43. RealType sd,
  44. RealType hint = 100);
  45. private:
  46. // Data member:
  47. RealType df_; // degrees of freedom is a real number > 0 or +infinity.
  48. };
  49. typedef students_t_distribution<double> students_t; // Convenience typedef for double version.
  50. template <class RealType, class Policy>
  51. inline const std::pair<RealType, RealType> range(const students_t_distribution<RealType, Policy>& /*dist*/)
  52. { // Range of permissible values for random variable x.
  53. // Now including infinity.
  54. using boost::math::tools::max_value;
  55. //return std::pair<RealType, RealType>(-max_value<RealType>(), max_value<RealType>());
  56. return std::pair<RealType, RealType>(((::std::numeric_limits<RealType>::is_specialized & ::std::numeric_limits<RealType>::has_infinity) ? -std::numeric_limits<RealType>::infinity() : -max_value<RealType>()), ((::std::numeric_limits<RealType>::is_specialized & ::std::numeric_limits<RealType>::has_infinity) ? +std::numeric_limits<RealType>::infinity() : +max_value<RealType>()));
  57. }
  58. template <class RealType, class Policy>
  59. inline const std::pair<RealType, RealType> support(const students_t_distribution<RealType, Policy>& /*dist*/)
  60. { // Range of supported values for random variable x.
  61. // Now including infinity.
  62. // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
  63. using boost::math::tools::max_value;
  64. //return std::pair<RealType, RealType>(-max_value<RealType>(), max_value<RealType>());
  65. return std::pair<RealType, RealType>(((::std::numeric_limits<RealType>::is_specialized & ::std::numeric_limits<RealType>::has_infinity) ? -std::numeric_limits<RealType>::infinity() : -max_value<RealType>()), ((::std::numeric_limits<RealType>::is_specialized & ::std::numeric_limits<RealType>::has_infinity) ? +std::numeric_limits<RealType>::infinity() : +max_value<RealType>()));
  66. }
  67. template <class RealType, class Policy>
  68. inline RealType pdf(const students_t_distribution<RealType, Policy>& dist, const RealType& x)
  69. {
  70. BOOST_FPU_EXCEPTION_GUARD
  71. BOOST_MATH_STD_USING // for ADL of std functions.
  72. RealType error_result;
  73. if(false == detail::check_x_not_NaN(
  74. "boost::math::pdf(const students_t_distribution<%1%>&, %1%)", x, &error_result, Policy()))
  75. return error_result;
  76. RealType df = dist.degrees_of_freedom();
  77. if(false == detail::check_df_gt0_to_inf( // Check that df > 0 or == +infinity.
  78. "boost::math::pdf(const students_t_distribution<%1%>&, %1%)", df, &error_result, Policy()))
  79. return error_result;
  80. RealType result;
  81. if ((boost::math::isinf)(x))
  82. { // - or +infinity.
  83. result = static_cast<RealType>(0);
  84. return result;
  85. }
  86. RealType limit = policies::get_epsilon<RealType, Policy>();
  87. // Use policies so that if policy requests lower precision,
  88. // then get the normal distribution approximation earlier.
  89. limit = static_cast<RealType>(1) / limit; // 1/eps
  90. // for 64-bit double 1/eps = 4503599627370496
  91. if (df > limit)
  92. { // Special case for really big degrees_of_freedom > 1 / eps
  93. // - use normal distribution which is much faster and more accurate.
  94. normal_distribution<RealType, Policy> n(0, 1);
  95. result = pdf(n, x);
  96. }
  97. else
  98. { //
  99. RealType basem1 = x * x / df;
  100. if(basem1 < 0.125)
  101. {
  102. result = exp(-boost::math::log1p(basem1, Policy()) * (1+df) / 2);
  103. }
  104. else
  105. {
  106. result = pow(1 / (1 + basem1), (df + 1) / 2);
  107. }
  108. result /= sqrt(df) * boost::math::beta(df / 2, RealType(0.5f), Policy());
  109. }
  110. return result;
  111. } // pdf
  112. template <class RealType, class Policy>
  113. inline RealType cdf(const students_t_distribution<RealType, Policy>& dist, const RealType& x)
  114. {
  115. RealType error_result;
  116. // degrees_of_freedom > 0 or infinity check:
  117. RealType df = dist.degrees_of_freedom();
  118. if (false == detail::check_df_gt0_to_inf( // Check that df > 0 or == +infinity.
  119. "boost::math::cdf(const students_t_distribution<%1%>&, %1%)", df, &error_result, Policy()))
  120. {
  121. return error_result;
  122. }
  123. // Check for bad x first.
  124. if(false == detail::check_x_not_NaN(
  125. "boost::math::cdf(const students_t_distribution<%1%>&, %1%)", x, &error_result, Policy()))
  126. {
  127. return error_result;
  128. }
  129. if (x == 0)
  130. { // Special case with exact result.
  131. return static_cast<RealType>(0.5);
  132. }
  133. if ((boost::math::isinf)(x))
  134. { // x == - or + infinity, regardless of df.
  135. return ((x < 0) ? static_cast<RealType>(0) : static_cast<RealType>(1));
  136. }
  137. RealType limit = policies::get_epsilon<RealType, Policy>();
  138. // Use policies so that if policy requests lower precision,
  139. // then get the normal distribution approximation earlier.
  140. limit = static_cast<RealType>(1) / limit; // 1/eps
  141. // for 64-bit double 1/eps = 4503599627370496
  142. if (df > limit)
  143. { // Special case for really big degrees_of_freedom > 1 / eps (perhaps infinite?)
  144. // - use normal distribution which is much faster and more accurate.
  145. normal_distribution<RealType, Policy> n(0, 1);
  146. RealType result = cdf(n, x);
  147. return result;
  148. }
  149. else
  150. { // normal df case.
  151. //
  152. // Calculate probability of Student's t using the incomplete beta function.
  153. // probability = ibeta(degrees_of_freedom / 2, 1/2, degrees_of_freedom / (degrees_of_freedom + t*t))
  154. //
  155. // However when t is small compared to the degrees of freedom, that formula
  156. // suffers from rounding error, use the identity formula to work around
  157. // the problem:
  158. //
  159. // I[x](a,b) = 1 - I[1-x](b,a)
  160. //
  161. // and:
  162. //
  163. // x = df / (df + t^2)
  164. //
  165. // so:
  166. //
  167. // 1 - x = t^2 / (df + t^2)
  168. //
  169. RealType x2 = x * x;
  170. RealType probability;
  171. if(df > 2 * x2)
  172. {
  173. RealType z = x2 / (df + x2);
  174. probability = ibetac(static_cast<RealType>(0.5), df / 2, z, Policy()) / 2;
  175. }
  176. else
  177. {
  178. RealType z = df / (df + x2);
  179. probability = ibeta(df / 2, static_cast<RealType>(0.5), z, Policy()) / 2;
  180. }
  181. return (x > 0 ? 1 - probability : probability);
  182. }
  183. } // cdf
  184. template <class RealType, class Policy>
  185. inline RealType quantile(const students_t_distribution<RealType, Policy>& dist, const RealType& p)
  186. {
  187. BOOST_MATH_STD_USING // for ADL of std functions
  188. //
  189. // Obtain parameters:
  190. RealType probability = p;
  191. // Check for domain errors:
  192. RealType df = dist.degrees_of_freedom();
  193. static const char* function = "boost::math::quantile(const students_t_distribution<%1%>&, %1%)";
  194. RealType error_result;
  195. if(false == (detail::check_df_gt0_to_inf( // Check that df > 0 or == +infinity.
  196. function, df, &error_result, Policy())
  197. && detail::check_probability(function, probability, &error_result, Policy())))
  198. return error_result;
  199. // Special cases, regardless of degrees_of_freedom.
  200. if (probability == 0)
  201. return -policies::raise_overflow_error<RealType>(function, 0, Policy());
  202. if (probability == 1)
  203. return policies::raise_overflow_error<RealType>(function, 0, Policy());
  204. if (probability == static_cast<RealType>(0.5))
  205. return 0; //
  206. //
  207. #if 0
  208. // This next block is disabled in favour of a faster method than
  209. // incomplete beta inverse, but code retained for future reference:
  210. //
  211. // Calculate quantile of Student's t using the incomplete beta function inverse:
  212. probability = (probability > 0.5) ? 1 - probability : probability;
  213. RealType t, x, y;
  214. x = ibeta_inv(degrees_of_freedom / 2, RealType(0.5), 2 * probability, &y);
  215. if(degrees_of_freedom * y > tools::max_value<RealType>() * x)
  216. t = tools::overflow_error<RealType>(function);
  217. else
  218. t = sqrt(degrees_of_freedom * y / x);
  219. //
  220. // Figure out sign based on the size of p:
  221. //
  222. if(p < 0.5)
  223. t = -t;
  224. return t;
  225. #endif
  226. //
  227. // Depending on how many digits RealType has, this may forward
  228. // to the incomplete beta inverse as above. Otherwise uses a
  229. // faster method that is accurate to ~15 digits everywhere
  230. // and a couple of epsilon at double precision and in the central
  231. // region where most use cases will occur...
  232. //
  233. return boost::math::detail::fast_students_t_quantile(df, probability, Policy());
  234. } // quantile
  235. template <class RealType, class Policy>
  236. inline RealType cdf(const complemented2_type<students_t_distribution<RealType, Policy>, RealType>& c)
  237. {
  238. return cdf(c.dist, -c.param);
  239. }
  240. template <class RealType, class Policy>
  241. inline RealType quantile(const complemented2_type<students_t_distribution<RealType, Policy>, RealType>& c)
  242. {
  243. return -quantile(c.dist, c.param);
  244. }
  245. //
  246. // Parameter estimation follows:
  247. //
  248. namespace detail{
  249. //
  250. // Functors for finding degrees of freedom:
  251. //
  252. template <class RealType, class Policy>
  253. struct sample_size_func
  254. {
  255. sample_size_func(RealType a, RealType b, RealType s, RealType d)
  256. : alpha(a), beta(b), ratio(s*s/(d*d)) {}
  257. RealType operator()(const RealType& df)
  258. {
  259. if(df <= tools::min_value<RealType>())
  260. { //
  261. return 1;
  262. }
  263. students_t_distribution<RealType, Policy> t(df);
  264. RealType qa = quantile(complement(t, alpha));
  265. RealType qb = quantile(complement(t, beta));
  266. qa += qb;
  267. qa *= qa;
  268. qa *= ratio;
  269. qa -= (df + 1);
  270. return qa;
  271. }
  272. RealType alpha, beta, ratio;
  273. };
  274. } // namespace detail
  275. template <class RealType, class Policy>
  276. RealType students_t_distribution<RealType, Policy>::find_degrees_of_freedom(
  277. RealType difference_from_mean,
  278. RealType alpha,
  279. RealType beta,
  280. RealType sd,
  281. RealType hint)
  282. {
  283. static const char* function = "boost::math::students_t_distribution<%1%>::find_degrees_of_freedom";
  284. //
  285. // Check for domain errors:
  286. //
  287. RealType error_result;
  288. if(false == detail::check_probability(
  289. function, alpha, &error_result, Policy())
  290. && detail::check_probability(function, beta, &error_result, Policy()))
  291. return error_result;
  292. if(hint <= 0)
  293. hint = 1;
  294. detail::sample_size_func<RealType, Policy> f(alpha, beta, sd, difference_from_mean);
  295. tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>());
  296. boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
  297. std::pair<RealType, RealType> r = tools::bracket_and_solve_root(f, hint, RealType(2), false, tol, max_iter, Policy());
  298. RealType result = r.first + (r.second - r.first) / 2;
  299. if(max_iter >= policies::get_max_root_iterations<Policy>())
  300. {
  301. return policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:"
  302. " either there is no answer to how many degrees of freedom are required"
  303. " or the answer is infinite. Current best guess is %1%", result, Policy());
  304. }
  305. return result;
  306. }
  307. template <class RealType, class Policy>
  308. inline RealType mode(const students_t_distribution<RealType, Policy>& /*dist*/)
  309. {
  310. // Assume no checks on degrees of freedom are useful (unlike mean).
  311. return 0; // Always zero by definition.
  312. }
  313. template <class RealType, class Policy>
  314. inline RealType median(const students_t_distribution<RealType, Policy>& /*dist*/)
  315. {
  316. // Assume no checks on degrees of freedom are useful (unlike mean).
  317. return 0; // Always zero by definition.
  318. }
  319. // See section 5.1 on moments at http://en.wikipedia.org/wiki/Student%27s_t-distribution
  320. template <class RealType, class Policy>
  321. inline RealType mean(const students_t_distribution<RealType, Policy>& dist)
  322. { // Revised for https://svn.boost.org/trac/boost/ticket/7177
  323. RealType df = dist.degrees_of_freedom();
  324. if(((boost::math::isnan)(df)) || (df <= 1) )
  325. { // mean is undefined for moment <= 1!
  326. return policies::raise_domain_error<RealType>(
  327. "boost::math::mean(students_t_distribution<%1%> const&, %1%)",
  328. "Mean is undefined for degrees of freedom < 1 but got %1%.", df, Policy());
  329. return std::numeric_limits<RealType>::quiet_NaN();
  330. }
  331. return 0;
  332. } // mean
  333. template <class RealType, class Policy>
  334. inline RealType variance(const students_t_distribution<RealType, Policy>& dist)
  335. { // http://en.wikipedia.org/wiki/Student%27s_t-distribution
  336. // Revised for https://svn.boost.org/trac/boost/ticket/7177
  337. RealType df = dist.degrees_of_freedom();
  338. if ((boost::math::isnan)(df) || (df <= 2))
  339. { // NaN or undefined for <= 2.
  340. return policies::raise_domain_error<RealType>(
  341. "boost::math::variance(students_t_distribution<%1%> const&, %1%)",
  342. "variance is undefined for degrees of freedom <= 2, but got %1%.",
  343. df, Policy());
  344. return std::numeric_limits<RealType>::quiet_NaN(); // Undefined.
  345. }
  346. if ((boost::math::isinf)(df))
  347. { // +infinity.
  348. return 1;
  349. }
  350. RealType limit = policies::get_epsilon<RealType, Policy>();
  351. // Use policies so that if policy requests lower precision,
  352. // then get the normal distribution approximation earlier.
  353. limit = static_cast<RealType>(1) / limit; // 1/eps
  354. // for 64-bit double 1/eps = 4503599627370496
  355. if (df > limit)
  356. { // Special case for really big degrees_of_freedom > 1 / eps.
  357. return 1;
  358. }
  359. else
  360. {
  361. return df / (df - 2);
  362. }
  363. } // variance
  364. template <class RealType, class Policy>
  365. inline RealType skewness(const students_t_distribution<RealType, Policy>& dist)
  366. {
  367. RealType df = dist.degrees_of_freedom();
  368. if( ((boost::math::isnan)(df)) || (dist.degrees_of_freedom() <= 3))
  369. { // Undefined for moment k = 3.
  370. return policies::raise_domain_error<RealType>(
  371. "boost::math::skewness(students_t_distribution<%1%> const&, %1%)",
  372. "Skewness is undefined for degrees of freedom <= 3, but got %1%.",
  373. dist.degrees_of_freedom(), Policy());
  374. return std::numeric_limits<RealType>::quiet_NaN();
  375. }
  376. return 0; // For all valid df, including infinity.
  377. } // skewness
  378. template <class RealType, class Policy>
  379. inline RealType kurtosis(const students_t_distribution<RealType, Policy>& dist)
  380. {
  381. RealType df = dist.degrees_of_freedom();
  382. if(((boost::math::isnan)(df)) || (df <= 4))
  383. { // Undefined or infinity for moment k = 4.
  384. return policies::raise_domain_error<RealType>(
  385. "boost::math::kurtosis(students_t_distribution<%1%> const&, %1%)",
  386. "Kurtosis is undefined for degrees of freedom <= 4, but got %1%.",
  387. df, Policy());
  388. return std::numeric_limits<RealType>::quiet_NaN(); // Undefined.
  389. }
  390. if ((boost::math::isinf)(df))
  391. { // +infinity.
  392. return 3;
  393. }
  394. RealType limit = policies::get_epsilon<RealType, Policy>();
  395. // Use policies so that if policy requests lower precision,
  396. // then get the normal distribution approximation earlier.
  397. limit = static_cast<RealType>(1) / limit; // 1/eps
  398. // for 64-bit double 1/eps = 4503599627370496
  399. if (df > limit)
  400. { // Special case for really big degrees_of_freedom > 1 / eps.
  401. return 3;
  402. }
  403. else
  404. {
  405. //return 3 * (df - 2) / (df - 4); re-arranged to
  406. return 6 / (df - 4) + 3;
  407. }
  408. } // kurtosis
  409. template <class RealType, class Policy>
  410. inline RealType kurtosis_excess(const students_t_distribution<RealType, Policy>& dist)
  411. {
  412. // see http://mathworld.wolfram.com/Kurtosis.html
  413. RealType df = dist.degrees_of_freedom();
  414. if(((boost::math::isnan)(df)) || (df <= 4))
  415. { // Undefined or infinity for moment k = 4.
  416. return policies::raise_domain_error<RealType>(
  417. "boost::math::kurtosis_excess(students_t_distribution<%1%> const&, %1%)",
  418. "Kurtosis_excess is undefined for degrees of freedom <= 4, but got %1%.",
  419. df, Policy());
  420. return std::numeric_limits<RealType>::quiet_NaN(); // Undefined.
  421. }
  422. if ((boost::math::isinf)(df))
  423. { // +infinity.
  424. return 0;
  425. }
  426. RealType limit = policies::get_epsilon<RealType, Policy>();
  427. // Use policies so that if policy requests lower precision,
  428. // then get the normal distribution approximation earlier.
  429. limit = static_cast<RealType>(1) / limit; // 1/eps
  430. // for 64-bit double 1/eps = 4503599627370496
  431. if (df > limit)
  432. { // Special case for really big degrees_of_freedom > 1 / eps.
  433. return 0;
  434. }
  435. else
  436. {
  437. return 6 / (df - 4);
  438. }
  439. }
  440. } // namespace math
  441. } // namespace boost
  442. #ifdef BOOST_MSVC
  443. # pragma warning(pop)
  444. #endif
  445. // This include must be at the end, *after* the accessors
  446. // for this distribution have been defined, in order to
  447. // keep compilers that support two-phase lookup happy.
  448. #include <boost/math/distributions/detail/derived_accessors.hpp>
  449. #endif // BOOST_STATS_STUDENTS_T_HPP