Chris@16: // boost\math\distributions\non_central_t.hpp Chris@16: Chris@16: // Copyright John Maddock 2008. Chris@16: Chris@16: // Use, modification and distribution are subject to the Chris@16: // Boost Software License, Version 1.0. Chris@16: // (See accompanying file LICENSE_1_0.txt Chris@16: // or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #ifndef BOOST_MATH_SPECIAL_NON_CENTRAL_T_HPP Chris@16: #define BOOST_MATH_SPECIAL_NON_CENTRAL_T_HPP Chris@16: Chris@16: #include Chris@16: #include // for nc beta Chris@16: #include // for normal CDF and quantile Chris@16: #include Chris@16: #include // quantile Chris@16: Chris@16: namespace boost Chris@16: { Chris@16: namespace math Chris@16: { Chris@16: Chris@16: template Chris@16: class non_central_t_distribution; Chris@16: Chris@16: namespace detail{ Chris@16: Chris@16: template Chris@16: T non_central_t2_p(T v, T delta, T x, T y, const Policy& pol, T init_val) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: // Chris@16: // Variables come first: Chris@16: // Chris@16: boost::uintmax_t max_iter = policies::get_max_series_iterations(); Chris@16: T errtol = policies::get_epsilon(); Chris@16: T d2 = delta * delta / 2; Chris@16: // Chris@16: // k is the starting point for iteration, and is the Chris@16: // maximum of the poisson weighting term, we don't Chris@16: // ever allow k == 0 as this can lead to catastrophic Chris@16: // cancellation errors later (test case is v = 1621286869049072.3 Chris@16: // delta = 0.16212868690490723, x = 0.86987415482475994). Chris@16: // Chris@16: int k = itrunc(d2); Chris@16: T pois; Chris@16: if(k == 0) k = 1; Chris@16: // Starting Poisson weight: Chris@16: pois = gamma_p_derivative(T(k+1), d2, pol) Chris@16: * tgamma_delta_ratio(T(k + 1), T(0.5f)) Chris@16: * delta / constants::root_two(); Chris@16: if(pois == 0) Chris@16: return init_val; Chris@16: T xterm, beta; Chris@16: // Recurrance & starting beta terms: Chris@16: beta = x < y Chris@16: ? detail::ibeta_imp(T(k + 1), T(v / 2), x, pol, false, true, &xterm) Chris@16: : detail::ibeta_imp(T(v / 2), T(k + 1), y, pol, true, true, &xterm); Chris@16: xterm *= y / (v / 2 + k); Chris@16: T poisf(pois), betaf(beta), xtermf(xterm); Chris@16: T sum = init_val; Chris@16: if((xterm == 0) && (beta == 0)) Chris@16: return init_val; Chris@16: Chris@16: // Chris@16: // Backwards recursion first, this is the stable Chris@16: // direction for recursion: Chris@16: // Chris@16: boost::uintmax_t count = 0; Chris@16: T last_term = 0; Chris@16: for(int i = k; i >= 0; --i) Chris@16: { Chris@16: T term = beta * pois; Chris@16: sum += term; Chris@16: // Don't terminate on first term in case we "fixed" k above: Chris@16: if((fabs(last_term) > fabs(term)) && fabs(term/sum) < errtol) Chris@16: break; Chris@16: last_term = term; Chris@16: pois *= (i + 0.5f) / d2; Chris@16: beta += xterm; Chris@16: xterm *= (i) / (x * (v / 2 + i - 1)); Chris@16: ++count; Chris@16: } Chris@16: last_term = 0; Chris@16: for(int i = k + 1; ; ++i) Chris@16: { Chris@16: poisf *= d2 / (i + 0.5f); Chris@16: xtermf *= (x * (v / 2 + i - 1)) / (i); Chris@16: betaf -= xtermf; Chris@16: T term = poisf * betaf; Chris@16: sum += term; Chris@16: if((fabs(last_term) > fabs(term)) && (fabs(term/sum) < errtol)) Chris@16: break; Chris@16: last_term = term; Chris@16: ++count; Chris@16: if(count > max_iter) Chris@16: { Chris@16: return policies::raise_evaluation_error( Chris@16: "cdf(non_central_t_distribution<%1%>, %1%)", Chris@16: "Series did not converge, closest value was %1%", sum, pol); Chris@16: } Chris@16: } Chris@16: return sum; Chris@16: } Chris@16: Chris@16: template Chris@16: T non_central_t2_q(T v, T delta, T x, T y, const Policy& pol, T init_val) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: // Chris@16: // Variables come first: Chris@16: // Chris@16: boost::uintmax_t max_iter = policies::get_max_series_iterations(); Chris@16: T errtol = boost::math::policies::get_epsilon(); Chris@16: T d2 = delta * delta / 2; Chris@16: // Chris@16: // k is the starting point for iteration, and is the Chris@16: // maximum of the poisson weighting term, we don't allow Chris@16: // k == 0 as this can cause catastrophic cancellation errors Chris@16: // (test case is v = 561908036470413.25, delta = 0.056190803647041321, Chris@16: // x = 1.6155232703966216): Chris@16: // Chris@16: int k = itrunc(d2); Chris@16: if(k == 0) k = 1; Chris@16: // Starting Poisson weight: Chris@16: T pois; Chris@16: if((k < (int)(max_factorial::value)) && (d2 < tools::log_max_value()) && (log(d2) * k < tools::log_max_value())) Chris@16: { Chris@16: // Chris@16: // For small k we can optimise this calculation by using Chris@16: // a simpler reduced formula: Chris@16: // Chris@16: pois = exp(-d2); Chris@16: pois *= pow(d2, static_cast(k)); Chris@16: pois /= boost::math::tgamma(T(k + 1 + 0.5), pol); Chris@16: pois *= delta / constants::root_two(); Chris@16: } Chris@16: else Chris@16: { Chris@16: pois = gamma_p_derivative(T(k+1), d2, pol) Chris@16: * tgamma_delta_ratio(T(k + 1), T(0.5f)) Chris@16: * delta / constants::root_two(); Chris@16: } Chris@16: if(pois == 0) Chris@16: return init_val; Chris@16: // Recurance term: Chris@16: T xterm; Chris@16: T beta; Chris@16: // Starting beta term: Chris@16: if(k != 0) Chris@16: { Chris@16: beta = x < y Chris@16: ? detail::ibeta_imp(T(k + 1), T(v / 2), x, pol, true, true, &xterm) Chris@16: : detail::ibeta_imp(T(v / 2), T(k + 1), y, pol, false, true, &xterm); Chris@16: Chris@16: xterm *= y / (v / 2 + k); Chris@16: } Chris@16: else Chris@16: { Chris@16: beta = pow(y, v / 2); Chris@16: xterm = beta; Chris@16: } Chris@16: T poisf(pois), betaf(beta), xtermf(xterm); Chris@16: T sum = init_val; Chris@16: if((xterm == 0) && (beta == 0)) Chris@16: return init_val; Chris@16: Chris@16: // Chris@16: // Fused forward and backwards recursion: Chris@16: // Chris@16: boost::uintmax_t count = 0; Chris@16: T last_term = 0; Chris@16: for(int i = k + 1, j = k; ; ++i, --j) Chris@16: { Chris@16: poisf *= d2 / (i + 0.5f); Chris@16: xtermf *= (x * (v / 2 + i - 1)) / (i); Chris@16: betaf += xtermf; Chris@16: T term = poisf * betaf; Chris@16: Chris@16: if(j >= 0) Chris@16: { Chris@16: term += beta * pois; Chris@16: pois *= (j + 0.5f) / d2; Chris@16: beta -= xterm; Chris@16: xterm *= (j) / (x * (v / 2 + j - 1)); Chris@16: } Chris@16: Chris@16: sum += term; Chris@16: // Don't terminate on first term in case we "fixed" the value of k above: Chris@16: if((fabs(last_term) > fabs(term)) && fabs(term/sum) < errtol) Chris@16: break; Chris@16: last_term = term; Chris@16: if(count > max_iter) Chris@16: { Chris@16: return policies::raise_evaluation_error( Chris@16: "cdf(non_central_t_distribution<%1%>, %1%)", Chris@16: "Series did not converge, closest value was %1%", sum, pol); Chris@16: } Chris@16: ++count; Chris@16: } Chris@16: return sum; Chris@16: } Chris@16: Chris@16: template Chris@16: T non_central_t_cdf(T v, T delta, T t, bool invert, const Policy& pol) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: if ((boost::math::isinf)(v)) Chris@16: { // Infinite degrees of freedom, so use normal distribution located at delta. Chris@16: normal_distribution n(delta, 1); Chris@16: return cdf(n, t); Chris@16: } Chris@16: // Chris@16: // Otherwise, for t < 0 we have to use the reflection formula: Chris@16: if(t < 0) Chris@16: { Chris@16: t = -t; Chris@16: delta = -delta; Chris@16: invert = !invert; Chris@16: } Chris@16: if(fabs(delta / (4 * v)) < policies::get_epsilon()) Chris@16: { Chris@16: // Approximate with a Student's T centred on delta, Chris@16: // the crossover point is based on eq 2.6 from Chris@16: // "A Comparison of Approximations To Percentiles of the Chris@16: // Noncentral t-Distribution". H. Sahai and M. M. Ojeda, Chris@16: // Revista Investigacion Operacional Vol 21, No 2, 2000. Chris@16: // Original sources referenced in the above are: Chris@16: // "Some Approximations to the Percentage Points of the Noncentral Chris@16: // t-Distribution". C. van Eeden. International Statistical Review, 29, 4-31. Chris@16: // "Continuous Univariate Distributions". N.L. Johnson, S. Kotz and Chris@16: // N. Balkrishnan. 1995. John Wiley and Sons New York. Chris@16: T result = cdf(students_t_distribution(v), t - delta); Chris@16: return invert ? 1 - result : result; Chris@16: } Chris@16: // Chris@16: // x and y are the corresponding random Chris@16: // variables for the noncentral beta distribution, Chris@16: // with y = 1 - x: Chris@16: // Chris@16: T x = t * t / (v + t * t); Chris@16: T y = v / (v + t * t); Chris@16: T d2 = delta * delta; Chris@16: T a = 0.5f; Chris@16: T b = v / 2; Chris@16: T c = a + b + d2 / 2; Chris@16: // Chris@16: // Crossover point for calculating p or q is the same Chris@16: // as for the noncentral beta: Chris@16: // Chris@16: T cross = 1 - (b / c) * (1 + d2 / (2 * c * c)); Chris@16: T result; Chris@16: if(x < cross) Chris@16: { Chris@16: // Chris@16: // Calculate p: Chris@16: // Chris@16: if(x != 0) Chris@16: { Chris@16: result = non_central_beta_p(a, b, d2, x, y, pol); Chris@16: result = non_central_t2_p(v, delta, x, y, pol, result); Chris@16: result /= 2; Chris@16: } Chris@16: else Chris@16: result = 0; Chris@16: result += cdf(boost::math::normal_distribution(), -delta); Chris@16: } Chris@16: else Chris@16: { Chris@16: // Chris@16: // Calculate q: Chris@16: // Chris@16: invert = !invert; Chris@16: if(x != 0) Chris@16: { Chris@16: result = non_central_beta_q(a, b, d2, x, y, pol); Chris@16: result = non_central_t2_q(v, delta, x, y, pol, result); Chris@16: result /= 2; Chris@16: } Chris@16: else // x == 0 Chris@16: result = cdf(complement(boost::math::normal_distribution(), -delta)); Chris@16: } Chris@16: if(invert) Chris@16: result = 1 - result; Chris@16: return result; Chris@16: } Chris@16: Chris@16: template Chris@16: T non_central_t_quantile(const char* function, T v, T delta, T p, T q, const Policy&) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: // static const char* function = "quantile(non_central_t_distribution<%1%>, %1%)"; Chris@16: // now passed as function Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: Chris@16: T r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: delta, Chris@16: &r, Chris@16: Policy()) Chris@16: || Chris@16: !detail::check_probability( Chris@16: function, Chris@16: p, Chris@16: &r, Chris@16: Policy())) Chris@16: return r; Chris@16: Chris@16: Chris@16: value_type guess = 0; Chris@16: if ( ((boost::math::isinf)(v)) || (v > 1 / boost::math::tools::epsilon()) ) Chris@16: { // Infinite or very large degrees of freedom, so use normal distribution located at delta. Chris@16: normal_distribution n(delta, 1); Chris@16: if (p < q) Chris@16: { Chris@16: return quantile(n, p); Chris@16: } Chris@16: else Chris@16: { Chris@16: return quantile(complement(n, q)); Chris@16: } Chris@16: } Chris@16: else if(v > 3) Chris@16: { // Use normal distribution to calculate guess. Chris@16: value_type mean = (v > 1 / policies::get_epsilon()) ? delta : delta * sqrt(v / 2) * tgamma_delta_ratio((v - 1) * 0.5f, T(0.5f)); Chris@16: value_type var = (v > 1 / policies::get_epsilon()) ? value_type(1) : (((delta * delta + 1) * v) / (v - 2) - mean * mean); Chris@16: if(p < q) Chris@16: guess = quantile(normal_distribution(mean, var), p); Chris@16: else Chris@16: guess = quantile(complement(normal_distribution(mean, var), q)); Chris@16: } Chris@16: // Chris@16: // We *must* get the sign of the initial guess correct, Chris@16: // or our root-finder will fail, so double check it now: Chris@16: // Chris@16: value_type pzero = non_central_t_cdf( Chris@16: static_cast(v), Chris@16: static_cast(delta), Chris@16: static_cast(0), Chris@16: !(p < q), Chris@16: forwarding_policy()); Chris@16: int s; Chris@16: if(p < q) Chris@16: s = boost::math::sign(p - pzero); Chris@16: else Chris@16: s = boost::math::sign(pzero - q); Chris@16: if(s != boost::math::sign(guess)) Chris@16: { Chris@16: guess = s; Chris@16: } Chris@16: Chris@16: value_type result = detail::generic_quantile( Chris@16: non_central_t_distribution(v, delta), Chris@16: (p < q ? p : q), Chris@16: guess, Chris@16: (p >= q), Chris@16: function); Chris@16: return policies::checked_narrowing_cast( Chris@16: result, Chris@16: function); Chris@16: } Chris@16: Chris@16: template Chris@16: T non_central_t2_pdf(T n, T delta, T x, T y, const Policy& pol, T init_val) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: // Chris@16: // Variables come first: Chris@16: // Chris@16: boost::uintmax_t max_iter = policies::get_max_series_iterations(); Chris@16: T errtol = boost::math::policies::get_epsilon(); Chris@16: T d2 = delta * delta / 2; Chris@16: // Chris@16: // k is the starting point for iteration, and is the Chris@16: // maximum of the poisson weighting term: Chris@16: // Chris@16: int k = itrunc(d2); Chris@16: T pois, xterm; Chris@16: if(k == 0) Chris@16: k = 1; Chris@16: // Starting Poisson weight: Chris@16: pois = gamma_p_derivative(T(k+1), d2, pol) Chris@16: * tgamma_delta_ratio(T(k + 1), T(0.5f)) Chris@16: * delta / constants::root_two(); Chris@16: // Starting beta term: Chris@16: xterm = x < y Chris@16: ? ibeta_derivative(T(k + 1), n / 2, x, pol) Chris@16: : ibeta_derivative(n / 2, T(k + 1), y, pol); Chris@16: T poisf(pois), xtermf(xterm); Chris@16: T sum = init_val; Chris@16: if((pois == 0) || (xterm == 0)) Chris@16: return init_val; Chris@16: Chris@16: // Chris@16: // Backwards recursion first, this is the stable Chris@16: // direction for recursion: Chris@16: // Chris@16: boost::uintmax_t count = 0; Chris@16: for(int i = k; i >= 0; --i) Chris@16: { Chris@16: T term = xterm * pois; Chris@16: sum += term; Chris@16: if(((fabs(term/sum) < errtol) && (i != k)) || (term == 0)) Chris@16: break; Chris@16: pois *= (i + 0.5f) / d2; Chris@16: xterm *= (i) / (x * (n / 2 + i)); Chris@16: ++count; Chris@16: if(count > max_iter) Chris@16: { Chris@16: return policies::raise_evaluation_error( Chris@16: "pdf(non_central_t_distribution<%1%>, %1%)", Chris@16: "Series did not converge, closest value was %1%", sum, pol); Chris@16: } Chris@16: } Chris@16: for(int i = k + 1; ; ++i) Chris@16: { Chris@16: poisf *= d2 / (i + 0.5f); Chris@16: xtermf *= (x * (n / 2 + i)) / (i); Chris@16: T term = poisf * xtermf; Chris@16: sum += term; Chris@16: if((fabs(term/sum) < errtol) || (term == 0)) Chris@16: break; Chris@16: ++count; Chris@16: if(count > max_iter) Chris@16: { Chris@16: return policies::raise_evaluation_error( Chris@16: "pdf(non_central_t_distribution<%1%>, %1%)", Chris@16: "Series did not converge, closest value was %1%", sum, pol); Chris@16: } Chris@16: } Chris@16: return sum; Chris@16: } Chris@16: Chris@16: template Chris@16: T non_central_t_pdf(T n, T delta, T t, const Policy& pol) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: if ((boost::math::isinf)(n)) Chris@16: { // Infinite degrees of freedom, so use normal distribution located at delta. Chris@16: normal_distribution norm(delta, 1); Chris@16: return pdf(norm, t); Chris@16: } Chris@16: // Chris@16: // Otherwise, for t < 0 we have to use the reflection formula: Chris@16: if(t < 0) Chris@16: { Chris@16: t = -t; Chris@16: delta = -delta; Chris@16: } Chris@16: if(t == 0) Chris@16: { Chris@16: // Chris@16: // Handle this as a special case, using the formula Chris@16: // from Weisstein, Eric W. Chris@16: // "Noncentral Student's t-Distribution." Chris@16: // From MathWorld--A Wolfram Web Resource. Chris@16: // http://mathworld.wolfram.com/NoncentralStudentst-Distribution.html Chris@16: // Chris@16: // The formula is simplified thanks to the relation Chris@16: // 1F1(a,b,0) = 1. Chris@16: // Chris@16: return tgamma_delta_ratio(n / 2 + 0.5f, T(0.5f)) Chris@16: * sqrt(n / constants::pi()) Chris@16: * exp(-delta * delta / 2) / 2; Chris@16: } Chris@16: if(fabs(delta / (4 * n)) < policies::get_epsilon()) Chris@16: { Chris@16: // Approximate with a Student's T centred on delta, Chris@16: // the crossover point is based on eq 2.6 from Chris@16: // "A Comparison of Approximations To Percentiles of the Chris@16: // Noncentral t-Distribution". H. Sahai and M. M. Ojeda, Chris@16: // Revista Investigacion Operacional Vol 21, No 2, 2000. Chris@16: // Original sources referenced in the above are: Chris@16: // "Some Approximations to the Percentage Points of the Noncentral Chris@16: // t-Distribution". C. van Eeden. International Statistical Review, 29, 4-31. Chris@16: // "Continuous Univariate Distributions". N.L. Johnson, S. Kotz and Chris@16: // N. Balkrishnan. 1995. John Wiley and Sons New York. Chris@16: return pdf(students_t_distribution(n), t - delta); Chris@16: } Chris@16: // Chris@16: // x and y are the corresponding random Chris@16: // variables for the noncentral beta distribution, Chris@16: // with y = 1 - x: Chris@16: // Chris@16: T x = t * t / (n + t * t); Chris@16: T y = n / (n + t * t); Chris@16: T a = 0.5f; Chris@16: T b = n / 2; Chris@16: T d2 = delta * delta; Chris@16: // Chris@16: // Calculate pdf: Chris@16: // Chris@16: T dt = n * t / (n * n + 2 * n * t * t + t * t * t * t); Chris@16: T result = non_central_beta_pdf(a, b, d2, x, y, pol); Chris@16: T tol = tools::epsilon() * result * 500; Chris@16: result = non_central_t2_pdf(n, delta, x, y, pol, result); Chris@16: if(result <= tol) Chris@16: result = 0; Chris@16: result *= dt; Chris@16: return result; Chris@16: } Chris@16: Chris@16: template Chris@16: T mean(T v, T delta, const Policy& pol) Chris@16: { Chris@16: if ((boost::math::isinf)(v)) Chris@16: { Chris@16: return delta; Chris@16: } Chris@16: BOOST_MATH_STD_USING Chris@16: if (v > 1 / boost::math::tools::epsilon() ) Chris@16: { Chris@16: //normal_distribution n(delta, 1); Chris@16: //return boost::math::mean(n); Chris@16: return delta; Chris@16: } Chris@16: else Chris@16: { Chris@16: return delta * sqrt(v / 2) * tgamma_delta_ratio((v - 1) * 0.5f, T(0.5f), pol); Chris@16: } Chris@16: // Other moments use mean so using normal distribution is propagated. Chris@16: } Chris@16: Chris@16: template Chris@16: T variance(T v, T delta, const Policy& pol) Chris@16: { Chris@16: if ((boost::math::isinf)(v)) Chris@16: { Chris@16: return 1; Chris@16: } Chris@16: if (delta == 0) Chris@16: { // == Student's t Chris@16: return v / (v - 2); Chris@16: } Chris@16: T result = ((delta * delta + 1) * v) / (v - 2); Chris@16: T m = mean(v, delta, pol); Chris@16: result -= m * m; Chris@16: return result; Chris@16: } Chris@16: Chris@16: template Chris@16: T skewness(T v, T delta, const Policy& pol) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: if ((boost::math::isinf)(v)) Chris@16: { Chris@16: return 0; Chris@16: } Chris@16: if(delta == 0) Chris@16: { // == Student's t Chris@16: return 0; Chris@16: } Chris@16: T mean = boost::math::detail::mean(v, delta, pol); Chris@16: T l2 = delta * delta; Chris@16: T var = ((l2 + 1) * v) / (v - 2) - mean * mean; Chris@16: T result = -2 * var; Chris@16: result += v * (l2 + 2 * v - 3) / ((v - 3) * (v - 2)); Chris@16: result *= mean; Chris@16: result /= pow(var, T(1.5f)); Chris@16: return result; Chris@16: } Chris@16: Chris@16: template Chris@16: T kurtosis_excess(T v, T delta, const Policy& pol) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: if ((boost::math::isinf)(v)) Chris@16: { Chris@16: return 3; Chris@16: } Chris@16: if (delta == 0) Chris@16: { // == Student's t Chris@16: return 3; Chris@16: } Chris@16: T mean = boost::math::detail::mean(v, delta, pol); Chris@16: T l2 = delta * delta; Chris@16: T var = ((l2 + 1) * v) / (v - 2) - mean * mean; Chris@16: T result = -3 * var; Chris@16: result += v * (l2 * (v + 1) + 3 * (3 * v - 5)) / ((v - 3) * (v - 2)); Chris@16: result *= -mean * mean; Chris@16: result += v * v * (l2 * l2 + 6 * l2 + 3) / ((v - 4) * (v - 2)); Chris@16: result /= var * var; Chris@16: return result; Chris@16: } Chris@16: Chris@16: #if 0 Chris@16: // Chris@16: // This code is disabled, since there can be multiple answers to the Chris@16: // question, and it's not clear how to find the "right" one. Chris@16: // Chris@16: template Chris@16: struct t_degrees_of_freedom_finder Chris@16: { Chris@16: t_degrees_of_freedom_finder( Chris@16: RealType delta_, RealType x_, RealType p_, bool c) Chris@16: : delta(delta_), x(x_), p(p_), comp(c) {} Chris@16: Chris@16: RealType operator()(const RealType& v) Chris@16: { Chris@16: non_central_t_distribution d(v, delta); Chris@16: return comp ? Chris@16: p - cdf(complement(d, x)) Chris@16: : cdf(d, x) - p; Chris@16: } Chris@16: private: Chris@16: RealType delta; Chris@16: RealType x; Chris@16: RealType p; Chris@16: bool comp; Chris@16: }; Chris@16: Chris@16: template Chris@16: inline RealType find_t_degrees_of_freedom( Chris@16: RealType delta, RealType x, RealType p, RealType q, const Policy& pol) Chris@16: { Chris@16: const char* function = "non_central_t<%1%>::find_degrees_of_freedom"; Chris@16: if((p == 0) || (q == 0)) Chris@16: { Chris@16: // Chris@16: // Can't a thing if one of p and q is zero: Chris@16: // Chris@16: return policies::raise_evaluation_error(function, Chris@16: "Can't find degrees of freedom when the probability is 0 or 1, only possible answer is %1%", Chris@16: RealType(std::numeric_limits::quiet_NaN()), Policy()); Chris@16: } Chris@16: t_degrees_of_freedom_finder f(delta, x, p < q ? p : q, p < q ? false : true); Chris@16: tools::eps_tolerance tol(policies::digits()); Chris@16: boost::uintmax_t max_iter = policies::get_max_root_iterations(); Chris@16: // Chris@16: // Pick an initial guess: Chris@16: // Chris@16: RealType guess = 200; Chris@16: std::pair ir = tools::bracket_and_solve_root( Chris@16: f, guess, RealType(2), false, tol, max_iter, pol); Chris@16: RealType result = ir.first + (ir.second - ir.first) / 2; Chris@16: if(max_iter >= policies::get_max_root_iterations()) Chris@16: { Chris@101: return policies::raise_evaluation_error(function, "Unable to locate solution in a reasonable time:" Chris@16: " or there is no answer to problem. Current best guess is %1%", result, Policy()); Chris@16: } Chris@16: return result; Chris@16: } Chris@16: Chris@16: template Chris@16: struct t_non_centrality_finder Chris@16: { Chris@16: t_non_centrality_finder( Chris@16: RealType v_, RealType x_, RealType p_, bool c) Chris@16: : v(v_), x(x_), p(p_), comp(c) {} Chris@16: Chris@16: RealType operator()(const RealType& delta) Chris@16: { Chris@16: non_central_t_distribution d(v, delta); Chris@16: return comp ? Chris@16: p - cdf(complement(d, x)) Chris@16: : cdf(d, x) - p; Chris@16: } Chris@16: private: Chris@16: RealType v; Chris@16: RealType x; Chris@16: RealType p; Chris@16: bool comp; Chris@16: }; Chris@16: Chris@16: template Chris@16: inline RealType find_t_non_centrality( Chris@16: RealType v, RealType x, RealType p, RealType q, const Policy& pol) Chris@16: { Chris@16: const char* function = "non_central_t<%1%>::find_t_non_centrality"; Chris@16: if((p == 0) || (q == 0)) Chris@16: { Chris@16: // Chris@16: // Can't do a thing if one of p and q is zero: Chris@16: // Chris@16: return policies::raise_evaluation_error(function, Chris@16: "Can't find non-centrality parameter when the probability is 0 or 1, only possible answer is %1%", Chris@16: RealType(std::numeric_limits::quiet_NaN()), Policy()); Chris@16: } Chris@16: t_non_centrality_finder f(v, x, p < q ? p : q, p < q ? false : true); Chris@16: tools::eps_tolerance tol(policies::digits()); Chris@16: boost::uintmax_t max_iter = policies::get_max_root_iterations(); Chris@16: // Chris@16: // Pick an initial guess that we know is the right side of Chris@16: // zero: Chris@16: // Chris@16: RealType guess; Chris@16: if(f(0) < 0) Chris@16: guess = 1; Chris@16: else Chris@16: guess = -1; Chris@16: std::pair ir = tools::bracket_and_solve_root( Chris@16: f, guess, RealType(2), false, tol, max_iter, pol); Chris@16: RealType result = ir.first + (ir.second - ir.first) / 2; Chris@16: if(max_iter >= policies::get_max_root_iterations()) Chris@16: { Chris@101: return policies::raise_evaluation_error(function, "Unable to locate solution in a reasonable time:" Chris@16: " or there is no answer to problem. Current best guess is %1%", result, Policy()); Chris@16: } Chris@16: return result; Chris@16: } Chris@16: #endif Chris@16: } // namespace detail ====================================================================== Chris@16: Chris@16: template > Chris@16: class non_central_t_distribution Chris@16: { Chris@16: public: Chris@16: typedef RealType value_type; Chris@16: typedef Policy policy_type; Chris@16: Chris@16: non_central_t_distribution(RealType v_, RealType lambda) : v(v_), ncp(lambda) Chris@16: { Chris@16: const char* function = "boost::math::non_central_t_distribution<%1%>::non_central_t_distribution(%1%,%1%)"; Chris@16: RealType r; Chris@16: detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()); Chris@16: detail::check_finite( Chris@16: function, Chris@16: lambda, Chris@16: &r, Chris@16: Policy()); Chris@16: } // non_central_t_distribution constructor. Chris@16: Chris@16: RealType degrees_of_freedom() const Chris@16: { // Private data getter function. Chris@16: return v; Chris@16: } Chris@16: RealType non_centrality() const Chris@16: { // Private data getter function. Chris@16: return ncp; Chris@16: } Chris@16: #if 0 Chris@16: // Chris@16: // This code is disabled, since there can be multiple answers to the Chris@16: // question, and it's not clear how to find the "right" one. Chris@16: // Chris@16: static RealType find_degrees_of_freedom(RealType delta, RealType x, RealType p) Chris@16: { Chris@16: const char* function = "non_central_t<%1%>::find_degrees_of_freedom"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: value_type result = detail::find_t_degrees_of_freedom( Chris@16: static_cast(delta), Chris@16: static_cast(x), Chris@16: static_cast(p), Chris@16: static_cast(1-p), Chris@16: forwarding_policy()); Chris@16: return policies::checked_narrowing_cast( Chris@16: result, Chris@16: function); Chris@16: } Chris@16: template Chris@16: static RealType find_degrees_of_freedom(const complemented3_type& c) Chris@16: { Chris@16: const char* function = "non_central_t<%1%>::find_degrees_of_freedom"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: value_type result = detail::find_t_degrees_of_freedom( Chris@16: static_cast(c.dist), Chris@16: static_cast(c.param1), Chris@16: static_cast(1-c.param2), Chris@16: static_cast(c.param2), Chris@16: forwarding_policy()); Chris@16: return policies::checked_narrowing_cast( Chris@16: result, Chris@16: function); Chris@16: } Chris@16: static RealType find_non_centrality(RealType v, RealType x, RealType p) Chris@16: { Chris@16: const char* function = "non_central_t<%1%>::find_t_non_centrality"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: value_type result = detail::find_t_non_centrality( Chris@16: static_cast(v), Chris@16: static_cast(x), Chris@16: static_cast(p), Chris@16: static_cast(1-p), Chris@16: forwarding_policy()); Chris@16: return policies::checked_narrowing_cast( Chris@16: result, Chris@16: function); Chris@16: } Chris@16: template Chris@16: static RealType find_non_centrality(const complemented3_type& c) Chris@16: { Chris@16: const char* function = "non_central_t<%1%>::find_t_non_centrality"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: value_type result = detail::find_t_non_centrality( Chris@16: static_cast(c.dist), Chris@16: static_cast(c.param1), Chris@16: static_cast(1-c.param2), Chris@16: static_cast(c.param2), Chris@16: forwarding_policy()); Chris@16: return policies::checked_narrowing_cast( Chris@16: result, Chris@16: function); Chris@16: } Chris@16: #endif Chris@16: private: Chris@16: // Data member, initialized by constructor. Chris@16: RealType v; // degrees of freedom Chris@16: RealType ncp; // non-centrality parameter Chris@16: }; // template class non_central_t_distribution Chris@16: Chris@16: typedef non_central_t_distribution non_central_t; // Reserved name of type double. Chris@16: Chris@16: // Non-member functions to give properties of the distribution. Chris@16: Chris@16: template Chris@16: inline const std::pair range(const non_central_t_distribution& /* dist */) Chris@16: { // Range of permissible values for random variable k. Chris@16: using boost::math::tools::max_value; Chris@16: return std::pair(-max_value(), max_value()); Chris@16: } Chris@16: Chris@16: template Chris@16: inline const std::pair support(const non_central_t_distribution& /* dist */) Chris@16: { // Range of supported values for random variable k. Chris@16: // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. Chris@16: using boost::math::tools::max_value; Chris@16: return std::pair(-max_value(), max_value()); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType mode(const non_central_t_distribution& dist) Chris@16: { // mode. Chris@16: static const char* function = "mode(non_central_t_distribution<%1%> const&)"; Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: RealType r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: l, Chris@16: &r, Chris@16: Policy())) Chris@16: return (RealType)r; Chris@16: Chris@16: BOOST_MATH_STD_USING Chris@16: Chris@16: RealType m = v < 3 ? 0 : detail::mean(v, l, Policy()); Chris@16: RealType var = v < 4 ? 1 : detail::variance(v, l, Policy()); Chris@16: Chris@16: return detail::generic_find_mode( Chris@16: dist, Chris@16: m, Chris@16: function, Chris@16: sqrt(var)); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType mean(const non_central_t_distribution& dist) Chris@16: { Chris@16: BOOST_MATH_STD_USING Chris@16: const char* function = "mean(const non_central_t_distribution<%1%>&)"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: RealType r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: l, Chris@16: &r, Chris@16: Policy())) Chris@16: return (RealType)r; Chris@16: if(v <= 1) Chris@16: return policies::raise_domain_error( Chris@16: function, Chris@16: "The non-central t distribution has no defined mean for degrees of freedom <= 1: got v=%1%.", v, Policy()); Chris@16: // return l * sqrt(v / 2) * tgamma_delta_ratio((v - 1) * 0.5f, RealType(0.5f)); Chris@16: return policies::checked_narrowing_cast( Chris@16: detail::mean(static_cast(v), static_cast(l), forwarding_policy()), function); Chris@16: Chris@16: } // mean Chris@16: Chris@16: template Chris@16: inline RealType variance(const non_central_t_distribution& dist) Chris@16: { // variance. Chris@16: const char* function = "variance(const non_central_t_distribution<%1%>&)"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: BOOST_MATH_STD_USING Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: RealType r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: l, Chris@16: &r, Chris@16: Policy())) Chris@16: return (RealType)r; Chris@16: if(v <= 2) Chris@16: return policies::raise_domain_error( Chris@16: function, Chris@16: "The non-central t distribution has no defined variance for degrees of freedom <= 2: got v=%1%.", v, Policy()); Chris@16: return policies::checked_narrowing_cast( Chris@16: detail::variance(static_cast(v), static_cast(l), forwarding_policy()), function); Chris@16: } Chris@16: Chris@16: // RealType standard_deviation(const non_central_t_distribution& dist) Chris@16: // standard_deviation provided by derived accessors. Chris@16: Chris@16: template Chris@16: inline RealType skewness(const non_central_t_distribution& dist) Chris@16: { // skewness = sqrt(l). Chris@16: const char* function = "skewness(const non_central_t_distribution<%1%>&)"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: RealType r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: l, Chris@16: &r, Chris@16: Policy())) Chris@16: return (RealType)r; Chris@16: if(v <= 3) Chris@16: return policies::raise_domain_error( Chris@16: function, Chris@16: "The non-central t distribution has no defined skewness for degrees of freedom <= 3: got v=%1%.", v, Policy());; Chris@16: return policies::checked_narrowing_cast( Chris@16: detail::skewness(static_cast(v), static_cast(l), forwarding_policy()), function); Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType kurtosis_excess(const non_central_t_distribution& dist) Chris@16: { Chris@16: const char* function = "kurtosis_excess(const non_central_t_distribution<%1%>&)"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: RealType r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: l, Chris@16: &r, Chris@16: Policy())) Chris@16: return (RealType)r; Chris@16: if(v <= 4) Chris@16: return policies::raise_domain_error( Chris@16: function, Chris@16: "The non-central t distribution has no defined kurtosis for degrees of freedom <= 4: got v=%1%.", v, Policy());; Chris@16: return policies::checked_narrowing_cast( Chris@16: detail::kurtosis_excess(static_cast(v), static_cast(l), forwarding_policy()), function); Chris@16: } // kurtosis_excess Chris@16: Chris@16: template Chris@16: inline RealType kurtosis(const non_central_t_distribution& dist) Chris@16: { Chris@16: return kurtosis_excess(dist) + 3; Chris@16: } Chris@16: Chris@16: template Chris@16: inline RealType pdf(const non_central_t_distribution& dist, const RealType& t) Chris@16: { // Probability Density/Mass Function. Chris@16: const char* function = "pdf(non_central_t_distribution<%1%>, %1%)"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: RealType r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: l, Chris@16: &r, Chris@16: Policy()) Chris@16: || Chris@16: !detail::check_x( Chris@16: function, Chris@16: t, Chris@16: &r, Chris@16: Policy())) Chris@16: return (RealType)r; Chris@16: return policies::checked_narrowing_cast( Chris@16: detail::non_central_t_pdf(static_cast(v), Chris@16: static_cast(l), Chris@16: static_cast(t), Chris@16: Policy()), Chris@16: function); Chris@16: } // pdf Chris@16: Chris@16: template Chris@16: RealType cdf(const non_central_t_distribution& dist, const RealType& x) Chris@16: { Chris@16: const char* function = "boost::math::cdf(non_central_t_distribution<%1%>&, %1%)"; Chris@16: // was const char* function = "boost::math::non_central_t_distribution<%1%>::cdf(%1%)"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: RealType r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: l, Chris@16: &r, Chris@16: Policy()) Chris@16: || Chris@16: !detail::check_x( Chris@16: function, Chris@16: x, Chris@16: &r, Chris@16: Policy())) Chris@16: return (RealType)r; Chris@16: if ((boost::math::isinf)(v)) Chris@16: { // Infinite degrees of freedom, so use normal distribution located at delta. Chris@16: normal_distribution n(l, 1); Chris@16: cdf(n, x); Chris@16: //return cdf(normal_distribution(l, 1), x); Chris@16: } Chris@16: Chris@16: if(l == 0) Chris@16: { // NO non-centrality, so use Student's t instead. Chris@16: return cdf(students_t_distribution(v), x); Chris@16: } Chris@16: return policies::checked_narrowing_cast( Chris@16: detail::non_central_t_cdf( Chris@16: static_cast(v), Chris@16: static_cast(l), Chris@16: static_cast(x), Chris@16: false, Policy()), Chris@16: function); Chris@16: } // cdf Chris@16: Chris@16: template Chris@16: RealType cdf(const complemented2_type, RealType>& c) Chris@16: { // Complemented Cumulative Distribution Function Chris@16: // was const char* function = "boost::math::non_central_t_distribution<%1%>::cdf(%1%)"; Chris@16: const char* function = "boost::math::cdf(const complement(non_central_t_distribution<%1%>&), %1%)"; Chris@16: typedef typename policies::evaluation::type value_type; Chris@16: typedef typename policies::normalise< Chris@16: Policy, Chris@16: policies::promote_float, Chris@16: policies::promote_double, Chris@16: policies::discrete_quantile<>, Chris@16: policies::assert_undefined<> >::type forwarding_policy; Chris@16: Chris@16: non_central_t_distribution const& dist = c.dist; Chris@16: RealType x = c.param; Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); // aka delta Chris@16: RealType r; Chris@16: if(!detail::check_df_gt0_to_inf( Chris@16: function, Chris@16: v, &r, Policy()) Chris@16: || Chris@16: !detail::check_finite( Chris@16: function, Chris@16: l, Chris@16: &r, Chris@16: Policy()) Chris@16: || Chris@16: !detail::check_x( Chris@16: function, Chris@16: x, Chris@16: &r, Chris@16: Policy())) Chris@16: return (RealType)r; Chris@16: Chris@16: if ((boost::math::isinf)(v)) Chris@16: { // Infinite degrees of freedom, so use normal distribution located at delta. Chris@16: normal_distribution n(l, 1); Chris@16: return cdf(complement(n, x)); Chris@16: } Chris@16: if(l == 0) Chris@16: { // zero non-centrality so use Student's t distribution. Chris@16: return cdf(complement(students_t_distribution(v), x)); Chris@16: } Chris@16: return policies::checked_narrowing_cast( Chris@16: detail::non_central_t_cdf( Chris@16: static_cast(v), Chris@16: static_cast(l), Chris@16: static_cast(x), Chris@16: true, Policy()), Chris@16: function); Chris@16: } // ccdf Chris@16: Chris@16: template Chris@16: inline RealType quantile(const non_central_t_distribution& dist, const RealType& p) Chris@16: { // Quantile (or Percent Point) function. Chris@16: static const char* function = "quantile(const non_central_t_distribution<%1%>, %1%)"; Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: return detail::non_central_t_quantile(function, v, l, p, RealType(1-p), Policy()); Chris@16: } // quantile Chris@16: Chris@16: template Chris@16: inline RealType quantile(const complemented2_type, RealType>& c) Chris@16: { // Quantile (or Percent Point) function. Chris@16: static const char* function = "quantile(const complement(non_central_t_distribution<%1%>, %1%))"; Chris@16: non_central_t_distribution const& dist = c.dist; Chris@16: RealType q = c.param; Chris@16: RealType v = dist.degrees_of_freedom(); Chris@16: RealType l = dist.non_centrality(); Chris@16: return detail::non_central_t_quantile(function, v, l, RealType(1-q), q, Policy()); Chris@16: } // quantile complement. Chris@16: Chris@16: } // namespace math Chris@16: } // namespace boost Chris@16: Chris@16: // This include must be at the end, *after* the accessors Chris@16: // for this distribution have been defined, in order to Chris@16: // keep compilers that support two-phase lookup happy. Chris@16: #include Chris@16: Chris@16: #endif // BOOST_MATH_SPECIAL_NON_CENTRAL_T_HPP Chris@16: