students_t.hpp 17 KB

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