You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
1126 lines
42 KiB
1126 lines
42 KiB
// boost\math\distributions\non_central_t.hpp |
|
|
|
// Copyright John Maddock 2008. |
|
|
|
// Use, modification and distribution are subject to the |
|
// Boost Software License, Version 1.0. |
|
// (See accompanying file LICENSE_1_0.txt |
|
// or copy at http://www.boost.org/LICENSE_1_0.txt) |
|
|
|
#ifndef BOOST_MATH_SPECIAL_NON_CENTRAL_T_HPP |
|
#define BOOST_MATH_SPECIAL_NON_CENTRAL_T_HPP |
|
|
|
#include <boost/math/distributions/fwd.hpp> |
|
#include <boost/math/distributions/non_central_beta.hpp> // for nc beta |
|
#include <boost/math/distributions/normal.hpp> // for normal CDF and quantile |
|
#include <boost/math/distributions/students_t.hpp> |
|
#include <boost/math/distributions/detail/generic_quantile.hpp> // quantile |
|
|
|
namespace boost |
|
{ |
|
namespace math |
|
{ |
|
|
|
template <class RealType, class Policy> |
|
class non_central_t_distribution; |
|
|
|
namespace detail{ |
|
|
|
template <class T, class Policy> |
|
T non_central_t2_p(T n, T delta, T x, T y, const Policy& pol, T init_val) |
|
{ |
|
BOOST_MATH_STD_USING |
|
// |
|
// Variables come first: |
|
// |
|
boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); |
|
T errtol = policies::get_epsilon<T, Policy>(); |
|
T d2 = delta * delta / 2; |
|
// |
|
// k is the starting point for iteration, and is the |
|
// maximum of the poisson weighting term: |
|
// |
|
int k = itrunc(d2); |
|
T pois; |
|
if(k < 15) |
|
{ |
|
// Since we'll likely need 30-40 terms anyway, start from zero |
|
// since this simplifies the arithmetic, don't go too overboard though |
|
// as this is the *unstable* direction: |
|
k = 0; |
|
// Starting Poisson weight: |
|
pois = exp(-d2) * 2 / constants::root_pi<T>(); |
|
pois *= delta / constants::root_two<T>(); |
|
} |
|
else |
|
{ |
|
// Starting Poisson weight: |
|
pois = gamma_p_derivative(T(k+1), d2, pol) |
|
* tgamma_delta_ratio(T(k + 1), T(0.5f)) |
|
* delta / constants::root_two<T>(); |
|
} |
|
if(pois == 0) |
|
return init_val; |
|
T xterm, beta; |
|
// Recurrance & starting beta terms: |
|
if(k == 0) |
|
{ |
|
beta = -boost::math::powm1(y, n / 2, pol); |
|
xterm = beta > 0.5f ? T(pow(y, n / 2)) : T(1 - beta); |
|
} |
|
else |
|
{ |
|
beta = x < y |
|
? detail::ibeta_imp(T(k + 1), T(n / 2), x, pol, false, true, &xterm) |
|
: detail::ibeta_imp(T(n / 2), T(k + 1), y, pol, true, true, &xterm); |
|
xterm *= y / (n / 2 + k); |
|
} |
|
T poisf(pois), betaf(beta), xtermf(xterm); |
|
T sum = init_val; |
|
if((xterm == 0) && (beta == 0)) |
|
return init_val; |
|
|
|
// |
|
// Backwards recursion first, this is the stable |
|
// direction for recursion: |
|
// |
|
boost::uintmax_t count = 0; |
|
for(int i = k; i >= 0; --i) |
|
{ |
|
T term = beta * pois; |
|
sum += term; |
|
if(fabs(term/sum) < errtol) |
|
break; |
|
pois *= (i + 0.5f) / d2; |
|
beta += xterm; |
|
xterm *= (i) / (x * (n / 2 + i - 1)); |
|
++count; |
|
} |
|
for(int i = k + 1; ; ++i) |
|
{ |
|
poisf *= d2 / (i + 0.5f); |
|
xtermf *= (x * (n / 2 + i - 1)) / (i); |
|
betaf -= xtermf; |
|
T term = poisf * betaf; |
|
sum += term; |
|
if(fabs(term/sum) < errtol) |
|
break; |
|
++count; |
|
if(count > max_iter) |
|
{ |
|
return policies::raise_evaluation_error( |
|
"cdf(non_central_t_distribution<%1%>, %1%)", |
|
"Series did not converge, closest value was %1%", sum, pol); |
|
} |
|
} |
|
return sum; |
|
} |
|
|
|
template <class T, class Policy> |
|
T non_central_t2_q(T n, T delta, T x, T y, const Policy& pol, T init_val) |
|
{ |
|
BOOST_MATH_STD_USING |
|
// |
|
// Variables come first: |
|
// |
|
boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); |
|
T errtol = boost::math::policies::get_epsilon<T, Policy>(); |
|
T d2 = delta * delta / 2; |
|
// |
|
// k is the starting point for iteration, and is the |
|
// maximum of the poisson weighting term: |
|
// |
|
int k = itrunc(d2); |
|
if(k < 30) |
|
{ |
|
// We typically need around 40 terms so may as well start at 0 |
|
// and gain faster computation of starting conditions: |
|
k = 0; |
|
} |
|
// Starting Poisson weight: |
|
T pois; |
|
if(k == 0) |
|
{ |
|
pois = exp(-d2) * 2 / constants::root_pi<T>(); |
|
pois *= delta / constants::root_two<T>(); |
|
} |
|
else if((k < (int)(max_factorial<T>::value)) && (d2 < tools::log_max_value<T>()) && (log(d2) * k < tools::log_max_value<T>())) |
|
{ |
|
// |
|
// For small k we can optimise this calculation by using |
|
// a simpler reduced formula: |
|
// |
|
pois = exp(-d2); |
|
pois *= pow(d2, static_cast<T>(k)); |
|
pois /= boost::math::tgamma(T(k + 1 + 0.5), pol); |
|
pois *= delta / constants::root_two<T>(); |
|
} |
|
else |
|
{ |
|
pois = gamma_p_derivative(T(k+1), d2, pol) |
|
* tgamma_delta_ratio(T(k + 1), T(0.5f)) |
|
* delta / constants::root_two<T>(); |
|
} |
|
if(pois == 0) |
|
return init_val; |
|
// Recurance term: |
|
T xterm; |
|
T beta; |
|
// Starting beta term: |
|
if(k != 0) |
|
{ |
|
beta = x < y |
|
? detail::ibeta_imp(T(k + 1), T(n / 2), x, pol, true, true, &xterm) |
|
: detail::ibeta_imp(T(n / 2), T(k + 1), y, pol, false, true, &xterm); |
|
|
|
xterm *= y / (n / 2 + k); |
|
} |
|
else |
|
{ |
|
beta = pow(y, n / 2); |
|
xterm = beta; |
|
} |
|
T poisf(pois), betaf(beta), xtermf(xterm); |
|
T sum = init_val; |
|
if((xterm == 0) && (beta == 0)) |
|
return init_val; |
|
|
|
// |
|
// Fused forward and backwards recursion: |
|
// |
|
boost::uintmax_t count = 0; |
|
for(int i = k + 1, j = k; ; ++i, --j) |
|
{ |
|
poisf *= d2 / (i + 0.5f); |
|
xtermf *= (x * (n / 2 + i - 1)) / (i); |
|
betaf += xtermf; |
|
T term = poisf * betaf; |
|
|
|
if(j >= 0) |
|
{ |
|
term += beta * pois; |
|
pois *= (j + 0.5f) / d2; |
|
beta -= xterm; |
|
xterm *= (j) / (x * (n / 2 + j - 1)); |
|
} |
|
|
|
sum += term; |
|
if(fabs(term/sum) < errtol) |
|
break; |
|
if(count > max_iter) |
|
{ |
|
return policies::raise_evaluation_error( |
|
"cdf(non_central_t_distribution<%1%>, %1%)", |
|
"Series did not converge, closest value was %1%", sum, pol); |
|
} |
|
++count; |
|
} |
|
return sum; |
|
} |
|
|
|
template <class T, class Policy> |
|
T non_central_t_cdf(T n, T delta, T t, bool invert, const Policy& pol) |
|
{ |
|
// |
|
// For t < 0 we have to use reflect: |
|
// |
|
if(t < 0) |
|
{ |
|
t = -t; |
|
delta = -delta; |
|
invert = !invert; |
|
} |
|
// |
|
// x and y are the corresponding random |
|
// variables for the noncentral beta distribution, |
|
// with y = 1 - x: |
|
// |
|
T x = t * t / (n + t * t); |
|
T y = n / (n + t * t); |
|
T d2 = delta * delta; |
|
T a = 0.5f; |
|
T b = n / 2; |
|
T c = a + b + d2 / 2; |
|
// |
|
// Crossover point for calculating p or q is the same |
|
// as for the noncentral beta: |
|
// |
|
T cross = 1 - (b / c) * (1 + d2 / (2 * c * c)); |
|
T result; |
|
if(x < cross) |
|
{ |
|
// |
|
// Calculate p: |
|
// |
|
if(x != 0) |
|
{ |
|
result = non_central_beta_p(a, b, d2, x, y, pol); |
|
result = non_central_t2_p(n, delta, x, y, pol, result); |
|
result /= 2; |
|
} |
|
else |
|
result = 0; |
|
result += cdf(boost::math::normal_distribution<T, Policy>(), -delta); |
|
} |
|
else |
|
{ |
|
// |
|
// Calculate q: |
|
// |
|
invert = !invert; |
|
if(x != 0) |
|
{ |
|
result = non_central_beta_q(a, b, d2, x, y, pol); |
|
result = non_central_t2_q(n, delta, x, y, pol, result); |
|
result /= 2; |
|
} |
|
else |
|
result = cdf(complement(boost::math::normal_distribution<T, Policy>(), -delta)); |
|
} |
|
if(invert) |
|
result = 1 - result; |
|
return result; |
|
} |
|
|
|
template <class T, class Policy> |
|
T non_central_t_quantile(T v, T delta, T p, T q, const Policy&) |
|
{ |
|
BOOST_MATH_STD_USING |
|
static const char* function = "quantile(non_central_t_distribution<%1%>, %1%)"; |
|
typedef typename policies::evaluation<T, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
|
|
T r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
delta, |
|
&r, |
|
Policy()) |
|
|| |
|
!detail::check_probability( |
|
function, |
|
p, |
|
&r, |
|
Policy())) |
|
return r; |
|
|
|
value_type guess = 0; |
|
if(v > 3) |
|
{ |
|
value_type mean = delta * sqrt(v / 2) * tgamma_delta_ratio((v - 1) * 0.5f, T(0.5f)); |
|
value_type var = ((delta * delta + 1) * v) / (v - 2) - mean * mean; |
|
if(p < q) |
|
guess = quantile(normal_distribution<value_type, forwarding_policy>(mean, var), p); |
|
else |
|
guess = quantile(complement(normal_distribution<value_type, forwarding_policy>(mean, var), q)); |
|
} |
|
// |
|
// We *must* get the sign of the initial guess correct, |
|
// or our root-finder will fail, so double check it now: |
|
// |
|
value_type pzero = non_central_t_cdf( |
|
static_cast<value_type>(v), |
|
static_cast<value_type>(delta), |
|
static_cast<value_type>(0), |
|
!(p < q), |
|
forwarding_policy()); |
|
int s; |
|
if(p < q) |
|
s = boost::math::sign(p - pzero); |
|
else |
|
s = boost::math::sign(pzero - q); |
|
if(s != boost::math::sign(guess)) |
|
{ |
|
guess = s; |
|
} |
|
|
|
value_type result = detail::generic_quantile( |
|
non_central_t_distribution<value_type, forwarding_policy>(v, delta), |
|
(p < q ? p : q), |
|
guess, |
|
(p >= q), |
|
function); |
|
return policies::checked_narrowing_cast<T, forwarding_policy>( |
|
result, |
|
function); |
|
} |
|
|
|
template <class T, class Policy> |
|
T non_central_t2_pdf(T n, T delta, T x, T y, const Policy& pol, T init_val) |
|
{ |
|
BOOST_MATH_STD_USING |
|
// |
|
// Variables come first: |
|
// |
|
boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); |
|
T errtol = boost::math::policies::get_epsilon<T, Policy>(); |
|
T d2 = delta * delta / 2; |
|
// |
|
// k is the starting point for iteration, and is the |
|
// maximum of the poisson weighting term: |
|
// |
|
int k = itrunc(d2); |
|
T pois, xterm; |
|
if(k < 30) |
|
{ |
|
// |
|
// Since we'll need at least 30-40 terms anyway, start from 0 |
|
// since this simplifies the starting arithmetic: |
|
// |
|
k = 0; |
|
// Starting Poisson weight: |
|
pois = exp(-d2) |
|
* (2 / constants::root_pi<T>()) |
|
* delta / constants::root_two<T>(); |
|
// Starting beta term: |
|
xterm = pow(y, n / 2 - 1) * n / 2; |
|
} |
|
else |
|
{ |
|
// Starting Poisson weight: |
|
pois = gamma_p_derivative(T(k+1), d2, pol) |
|
* tgamma_delta_ratio(T(k + 1), T(0.5f)) |
|
* delta / constants::root_two<T>(); |
|
// Starting beta term: |
|
xterm = x < y |
|
? ibeta_derivative(T(k + 1), n / 2, x, pol) |
|
: ibeta_derivative(n / 2, T(k + 1), y, pol); |
|
} |
|
T poisf(pois), xtermf(xterm); |
|
T sum = init_val; |
|
if((pois == 0) || (xterm == 0)) |
|
return init_val; |
|
|
|
// |
|
// Backwards recursion first, this is the stable |
|
// direction for recursion: |
|
// |
|
boost::uintmax_t count = 0; |
|
for(int i = k; i >= 0; --i) |
|
{ |
|
T term = xterm * pois; |
|
sum += term; |
|
if((fabs(term/sum) < errtol) || (term == 0)) |
|
break; |
|
pois *= (i + 0.5f) / d2; |
|
xterm *= (i) / (x * (n / 2 + i)); |
|
++count; |
|
if(count > max_iter) |
|
{ |
|
return policies::raise_evaluation_error( |
|
"pdf(non_central_t_distribution<%1%>, %1%)", |
|
"Series did not converge, closest value was %1%", sum, pol); |
|
} |
|
} |
|
for(int i = k + 1; ; ++i) |
|
{ |
|
poisf *= d2 / (i + 0.5f); |
|
xtermf *= (x * (n / 2 + i)) / (i); |
|
T term = poisf * xtermf; |
|
sum += term; |
|
if((fabs(term/sum) < errtol) || (term == 0)) |
|
break; |
|
++count; |
|
if(count > max_iter) |
|
{ |
|
return policies::raise_evaluation_error( |
|
"pdf(non_central_t_distribution<%1%>, %1%)", |
|
"Series did not converge, closest value was %1%", sum, pol); |
|
} |
|
} |
|
return sum; |
|
} |
|
|
|
template <class T, class Policy> |
|
T non_central_t_pdf(T n, T delta, T t, const Policy& pol) |
|
{ |
|
BOOST_MATH_STD_USING |
|
// |
|
// For t < 0 we have to use the reflection formula: |
|
// |
|
if(t < 0) |
|
{ |
|
t = -t; |
|
delta = -delta; |
|
} |
|
if(t == 0) |
|
{ |
|
// |
|
// Handle this as a special case, using the formula |
|
// from Weisstein, Eric W. |
|
// "Noncentral Student's t-Distribution." |
|
// From MathWorld--A Wolfram Web Resource. |
|
// http://mathworld.wolfram.com/NoncentralStudentst-Distribution.html |
|
// |
|
// The formula is simplified thanks to the relation |
|
// 1F1(a,b,0) = 1. |
|
// |
|
return tgamma_delta_ratio(n / 2 + 0.5f, T(0.5f)) |
|
* sqrt(n / constants::pi<T>()) |
|
* exp(-delta * delta / 2) / 2; |
|
} |
|
// |
|
// x and y are the corresponding random |
|
// variables for the noncentral beta distribution, |
|
// with y = 1 - x: |
|
// |
|
T x = t * t / (n + t * t); |
|
T y = n / (n + t * t); |
|
T a = 0.5f; |
|
T b = n / 2; |
|
T d2 = delta * delta; |
|
// |
|
// Calculate pdf: |
|
// |
|
T dt = n * t / (n * n + 2 * n * t * t + t * t * t * t); |
|
T result = non_central_beta_pdf(a, b, d2, x, y, pol); |
|
T tol = tools::epsilon<T>() * result * 500; |
|
result = non_central_t2_pdf(n, delta, x, y, pol, result); |
|
if(result <= tol) |
|
result = 0; |
|
result *= dt; |
|
return result; |
|
} |
|
|
|
template <class T, class Policy> |
|
T mean(T v, T delta, const Policy& pol) |
|
{ |
|
BOOST_MATH_STD_USING |
|
return delta * sqrt(v / 2) * tgamma_delta_ratio((v - 1) * 0.5f, T(0.5f), pol); |
|
} |
|
|
|
template <class T, class Policy> |
|
T variance(T v, T delta, const Policy& pol) |
|
{ |
|
T result = ((delta * delta + 1) * v) / (v - 2); |
|
T m = mean(v, delta, pol); |
|
result -= m * m; |
|
return result; |
|
} |
|
|
|
template <class T, class Policy> |
|
T skewness(T v, T delta, const Policy& pol) |
|
{ |
|
BOOST_MATH_STD_USING |
|
T mean = boost::math::detail::mean(v, delta, pol); |
|
T l2 = delta * delta; |
|
T var = ((l2 + 1) * v) / (v - 2) - mean * mean; |
|
T result = -2 * var; |
|
result += v * (l2 + 2 * v - 3) / ((v - 3) * (v - 2)); |
|
result *= mean; |
|
result /= pow(var, T(1.5f)); |
|
return result; |
|
} |
|
|
|
template <class T, class Policy> |
|
T kurtosis_excess(T v, T delta, const Policy& pol) |
|
{ |
|
BOOST_MATH_STD_USING |
|
T mean = boost::math::detail::mean(v, delta, pol); |
|
T l2 = delta * delta; |
|
T var = ((l2 + 1) * v) / (v - 2) - mean * mean; |
|
T result = -3 * var; |
|
result += v * (l2 * (v + 1) + 3 * (3 * v - 5)) / ((v - 3) * (v - 2)); |
|
result *= -mean * mean; |
|
result += v * v * (l2 * l2 + 6 * l2 + 3) / ((v - 4) * (v - 2)); |
|
result /= var * var; |
|
return result; |
|
} |
|
|
|
#if 0 |
|
// |
|
// This code is disabled, since there can be multiple answers to the |
|
// question, and it's not clear how to find the "right" one. |
|
// |
|
template <class RealType, class Policy> |
|
struct t_degrees_of_freedom_finder |
|
{ |
|
t_degrees_of_freedom_finder( |
|
RealType delta_, RealType x_, RealType p_, bool c) |
|
: delta(delta_), x(x_), p(p_), comp(c) {} |
|
|
|
RealType operator()(const RealType& v) |
|
{ |
|
non_central_t_distribution<RealType, Policy> d(v, delta); |
|
return comp ? |
|
p - cdf(complement(d, x)) |
|
: cdf(d, x) - p; |
|
} |
|
private: |
|
RealType delta; |
|
RealType x; |
|
RealType p; |
|
bool comp; |
|
}; |
|
|
|
template <class RealType, class Policy> |
|
inline RealType find_t_degrees_of_freedom( |
|
RealType delta, RealType x, RealType p, RealType q, const Policy& pol) |
|
{ |
|
const char* function = "non_central_t<%1%>::find_degrees_of_freedom"; |
|
if((p == 0) || (q == 0)) |
|
{ |
|
// |
|
// Can't a thing if one of p and q is zero: |
|
// |
|
return policies::raise_evaluation_error<RealType>(function, |
|
"Can't find degrees of freedom when the probability is 0 or 1, only possible answer is %1%", |
|
RealType(std::numeric_limits<RealType>::quiet_NaN()), Policy()); |
|
} |
|
t_degrees_of_freedom_finder<RealType, Policy> f(delta, x, p < q ? p : q, p < q ? false : true); |
|
tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>()); |
|
boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>(); |
|
// |
|
// Pick an initial guess: |
|
// |
|
RealType guess = 200; |
|
std::pair<RealType, RealType> ir = tools::bracket_and_solve_root( |
|
f, guess, RealType(2), false, tol, max_iter, pol); |
|
RealType result = ir.first + (ir.second - ir.first) / 2; |
|
if(max_iter >= policies::get_max_root_iterations<Policy>()) |
|
{ |
|
policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:" |
|
" or there is no answer to problem. Current best guess is %1%", result, Policy()); |
|
} |
|
return result; |
|
} |
|
|
|
template <class RealType, class Policy> |
|
struct t_non_centrality_finder |
|
{ |
|
t_non_centrality_finder( |
|
RealType v_, RealType x_, RealType p_, bool c) |
|
: v(v_), x(x_), p(p_), comp(c) {} |
|
|
|
RealType operator()(const RealType& delta) |
|
{ |
|
non_central_t_distribution<RealType, Policy> d(v, delta); |
|
return comp ? |
|
p - cdf(complement(d, x)) |
|
: cdf(d, x) - p; |
|
} |
|
private: |
|
RealType v; |
|
RealType x; |
|
RealType p; |
|
bool comp; |
|
}; |
|
|
|
template <class RealType, class Policy> |
|
inline RealType find_t_non_centrality( |
|
RealType v, RealType x, RealType p, RealType q, const Policy& pol) |
|
{ |
|
const char* function = "non_central_t<%1%>::find_t_non_centrality"; |
|
if((p == 0) || (q == 0)) |
|
{ |
|
// |
|
// Can't do a thing if one of p and q is zero: |
|
// |
|
return policies::raise_evaluation_error<RealType>(function, |
|
"Can't find non centrality parameter when the probability is 0 or 1, only possible answer is %1%", |
|
RealType(std::numeric_limits<RealType>::quiet_NaN()), Policy()); |
|
} |
|
t_non_centrality_finder<RealType, Policy> f(v, x, p < q ? p : q, p < q ? false : true); |
|
tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>()); |
|
boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>(); |
|
// |
|
// Pick an initial guess that we know is the right side of |
|
// zero: |
|
// |
|
RealType guess; |
|
if(f(0) < 0) |
|
guess = 1; |
|
else |
|
guess = -1; |
|
std::pair<RealType, RealType> ir = tools::bracket_and_solve_root( |
|
f, guess, RealType(2), false, tol, max_iter, pol); |
|
RealType result = ir.first + (ir.second - ir.first) / 2; |
|
if(max_iter >= policies::get_max_root_iterations<Policy>()) |
|
{ |
|
policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:" |
|
" or there is no answer to problem. Current best guess is %1%", result, Policy()); |
|
} |
|
return result; |
|
} |
|
#endif |
|
} // namespace detail |
|
|
|
template <class RealType = double, class Policy = policies::policy<> > |
|
class non_central_t_distribution |
|
{ |
|
public: |
|
typedef RealType value_type; |
|
typedef Policy policy_type; |
|
|
|
non_central_t_distribution(RealType v_, RealType lambda) : v(v_), ncp(lambda) |
|
{ |
|
const char* function = "boost::math::non_central_t_distribution<%1%>::non_central_t_distribution(%1%,%1%)"; |
|
RealType r; |
|
detail::check_df( |
|
function, |
|
v, &r, Policy()); |
|
detail::check_finite( |
|
function, |
|
lambda, |
|
&r, |
|
Policy()); |
|
} // non_central_t_distribution constructor. |
|
|
|
RealType degrees_of_freedom() const |
|
{ // Private data getter function. |
|
return v; |
|
} |
|
RealType non_centrality() const |
|
{ // Private data getter function. |
|
return ncp; |
|
} |
|
#if 0 |
|
// |
|
// This code is disabled, since there can be multiple answers to the |
|
// question, and it's not clear how to find the "right" one. |
|
// |
|
static RealType find_degrees_of_freedom(RealType delta, RealType x, RealType p) |
|
{ |
|
const char* function = "non_central_t<%1%>::find_degrees_of_freedom"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
value_type result = detail::find_t_degrees_of_freedom( |
|
static_cast<value_type>(delta), |
|
static_cast<value_type>(x), |
|
static_cast<value_type>(p), |
|
static_cast<value_type>(1-p), |
|
forwarding_policy()); |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
result, |
|
function); |
|
} |
|
template <class A, class B, class C> |
|
static RealType find_degrees_of_freedom(const complemented3_type<A,B,C>& c) |
|
{ |
|
const char* function = "non_central_t<%1%>::find_degrees_of_freedom"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
value_type result = detail::find_t_degrees_of_freedom( |
|
static_cast<value_type>(c.dist), |
|
static_cast<value_type>(c.param1), |
|
static_cast<value_type>(1-c.param2), |
|
static_cast<value_type>(c.param2), |
|
forwarding_policy()); |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
result, |
|
function); |
|
} |
|
static RealType find_non_centrality(RealType v, RealType x, RealType p) |
|
{ |
|
const char* function = "non_central_t<%1%>::find_t_non_centrality"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
value_type result = detail::find_t_non_centrality( |
|
static_cast<value_type>(v), |
|
static_cast<value_type>(x), |
|
static_cast<value_type>(p), |
|
static_cast<value_type>(1-p), |
|
forwarding_policy()); |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
result, |
|
function); |
|
} |
|
template <class A, class B, class C> |
|
static RealType find_non_centrality(const complemented3_type<A,B,C>& c) |
|
{ |
|
const char* function = "non_central_t<%1%>::find_t_non_centrality"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
value_type result = detail::find_t_non_centrality( |
|
static_cast<value_type>(c.dist), |
|
static_cast<value_type>(c.param1), |
|
static_cast<value_type>(1-c.param2), |
|
static_cast<value_type>(c.param2), |
|
forwarding_policy()); |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
result, |
|
function); |
|
} |
|
#endif |
|
private: |
|
// Data member, initialized by constructor. |
|
RealType v; // degrees of freedom |
|
RealType ncp; // non-centrality parameter |
|
}; // template <class RealType, class Policy> class non_central_t_distribution |
|
|
|
typedef non_central_t_distribution<double> non_central_t; // Reserved name of type double. |
|
|
|
// Non-member functions to give properties of the distribution. |
|
|
|
template <class RealType, class Policy> |
|
inline const std::pair<RealType, RealType> range(const non_central_t_distribution<RealType, Policy>& /* dist */) |
|
{ // Range of permissible values for random variable k. |
|
using boost::math::tools::max_value; |
|
return std::pair<RealType, RealType>(-max_value<RealType>(), max_value<RealType>()); |
|
} |
|
|
|
template <class RealType, class Policy> |
|
inline const std::pair<RealType, RealType> support(const non_central_t_distribution<RealType, Policy>& /* dist */) |
|
{ // Range of supported values for random variable k. |
|
// This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. |
|
using boost::math::tools::max_value; |
|
return std::pair<RealType, RealType>(-max_value<RealType>(), max_value<RealType>()); |
|
} |
|
|
|
template <class RealType, class Policy> |
|
inline RealType mode(const non_central_t_distribution<RealType, Policy>& dist) |
|
{ // mode. |
|
static const char* function = "mode(non_central_t_distribution<%1%> const&)"; |
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
RealType r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
l, |
|
&r, |
|
Policy())) |
|
return (RealType)r; |
|
|
|
BOOST_MATH_STD_USING |
|
|
|
RealType m = v < 3 ? 0 : detail::mean(v, l, Policy()); |
|
RealType var = v < 4 ? 1 : detail::variance(v, l, Policy()); |
|
|
|
return detail::generic_find_mode( |
|
dist, |
|
m, |
|
function, |
|
sqrt(var)); |
|
} |
|
|
|
template <class RealType, class Policy> |
|
inline RealType mean(const non_central_t_distribution<RealType, Policy>& dist) |
|
{ |
|
BOOST_MATH_STD_USING |
|
const char* function = "mean(const non_central_t_distribution<%1%>&)"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
RealType r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
l, |
|
&r, |
|
Policy())) |
|
return (RealType)r; |
|
if(v <= 1) |
|
return policies::raise_domain_error<RealType>( |
|
function, |
|
"The non central t distribution has no defined mean for degrees of freedom <= 1: got v=%1%.", v, Policy()); |
|
// return l * sqrt(v / 2) * tgamma_delta_ratio((v - 1) * 0.5f, RealType(0.5f)); |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
detail::mean(static_cast<value_type>(v), static_cast<value_type>(l), forwarding_policy()), function); |
|
|
|
} // mean |
|
|
|
template <class RealType, class Policy> |
|
inline RealType variance(const non_central_t_distribution<RealType, Policy>& dist) |
|
{ // variance. |
|
const char* function = "variance(const non_central_t_distribution<%1%>&)"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
BOOST_MATH_STD_USING |
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
RealType r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
l, |
|
&r, |
|
Policy())) |
|
return (RealType)r; |
|
if(v <= 2) |
|
return policies::raise_domain_error<RealType>( |
|
function, |
|
"The non central t distribution has no defined variance for degrees of freedom <= 2: got v=%1%.", v, Policy()); |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
detail::variance(static_cast<value_type>(v), static_cast<value_type>(l), forwarding_policy()), function); |
|
} |
|
|
|
// RealType standard_deviation(const non_central_t_distribution<RealType, Policy>& dist) |
|
// standard_deviation provided by derived accessors. |
|
|
|
template <class RealType, class Policy> |
|
inline RealType skewness(const non_central_t_distribution<RealType, Policy>& dist) |
|
{ // skewness = sqrt(l). |
|
const char* function = "skewness(const non_central_t_distribution<%1%>&)"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
RealType r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
l, |
|
&r, |
|
Policy())) |
|
return (RealType)r; |
|
if(v <= 3) |
|
return policies::raise_domain_error<RealType>( |
|
function, |
|
"The non central t distribution has no defined skewness for degrees of freedom <= 3: got v=%1%.", v, Policy());; |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
detail::skewness(static_cast<value_type>(v), static_cast<value_type>(l), forwarding_policy()), function); |
|
} |
|
|
|
template <class RealType, class Policy> |
|
inline RealType kurtosis_excess(const non_central_t_distribution<RealType, Policy>& dist) |
|
{ |
|
const char* function = "kurtosis_excess(const non_central_t_distribution<%1%>&)"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
RealType r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
l, |
|
&r, |
|
Policy())) |
|
return (RealType)r; |
|
if(v <= 4) |
|
return policies::raise_domain_error<RealType>( |
|
function, |
|
"The non central t distribution has no defined kurtosis for degrees of freedom <= 4: got v=%1%.", v, Policy());; |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
detail::kurtosis_excess(static_cast<value_type>(v), static_cast<value_type>(l), forwarding_policy()), function); |
|
} // kurtosis_excess |
|
|
|
template <class RealType, class Policy> |
|
inline RealType kurtosis(const non_central_t_distribution<RealType, Policy>& dist) |
|
{ |
|
return kurtosis_excess(dist) + 3; |
|
} |
|
|
|
template <class RealType, class Policy> |
|
inline RealType pdf(const non_central_t_distribution<RealType, Policy>& dist, const RealType& t) |
|
{ // Probability Density/Mass Function. |
|
const char* function = "cdf(non_central_t_distribution<%1%>, %1%)"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
|
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
RealType r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
l, |
|
&r, |
|
Policy()) |
|
|| |
|
!detail::check_x( |
|
function, |
|
t, |
|
&r, |
|
Policy())) |
|
return (RealType)r; |
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
detail::non_central_t_pdf(static_cast<value_type>(v), |
|
static_cast<value_type>(l), |
|
static_cast<value_type>(t), |
|
Policy()), |
|
function); |
|
} // pdf |
|
|
|
template <class RealType, class Policy> |
|
RealType cdf(const non_central_t_distribution<RealType, Policy>& dist, const RealType& x) |
|
{ |
|
const char* function = "boost::math::non_central_t_distribution<%1%>::cdf(%1%)"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
|
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
RealType r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
l, |
|
&r, |
|
Policy()) |
|
|| |
|
!detail::check_x( |
|
function, |
|
x, |
|
&r, |
|
Policy())) |
|
return (RealType)r; |
|
|
|
if(l == 0) |
|
return cdf(students_t_distribution<RealType, Policy>(v), x); |
|
|
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
detail::non_central_t_cdf( |
|
static_cast<value_type>(v), |
|
static_cast<value_type>(l), |
|
static_cast<value_type>(x), |
|
false, Policy()), |
|
function); |
|
} // cdf |
|
|
|
template <class RealType, class Policy> |
|
RealType cdf(const complemented2_type<non_central_t_distribution<RealType, Policy>, RealType>& c) |
|
{ // Complemented Cumulative Distribution Function |
|
const char* function = "boost::math::non_central_t_distribution<%1%>::cdf(%1%)"; |
|
typedef typename policies::evaluation<RealType, Policy>::type value_type; |
|
typedef typename policies::normalise< |
|
Policy, |
|
policies::promote_float<false>, |
|
policies::promote_double<false>, |
|
policies::discrete_quantile<>, |
|
policies::assert_undefined<> >::type forwarding_policy; |
|
|
|
non_central_t_distribution<RealType, Policy> const& dist = c.dist; |
|
RealType x = c.param; |
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
RealType r; |
|
if(!detail::check_df( |
|
function, |
|
v, &r, Policy()) |
|
|| |
|
!detail::check_finite( |
|
function, |
|
l, |
|
&r, |
|
Policy()) |
|
|| |
|
!detail::check_x( |
|
function, |
|
x, |
|
&r, |
|
Policy())) |
|
return (RealType)r; |
|
|
|
if(l == 0) |
|
return cdf(complement(students_t_distribution<RealType, Policy>(v), x)); |
|
|
|
return policies::checked_narrowing_cast<RealType, forwarding_policy>( |
|
detail::non_central_t_cdf( |
|
static_cast<value_type>(v), |
|
static_cast<value_type>(l), |
|
static_cast<value_type>(x), |
|
true, Policy()), |
|
function); |
|
} // ccdf |
|
|
|
template <class RealType, class Policy> |
|
inline RealType quantile(const non_central_t_distribution<RealType, Policy>& dist, const RealType& p) |
|
{ // Quantile (or Percent Point) function. |
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
return detail::non_central_t_quantile(v, l, p, RealType(1-p), Policy()); |
|
} // quantile |
|
|
|
template <class RealType, class Policy> |
|
inline RealType quantile(const complemented2_type<non_central_t_distribution<RealType, Policy>, RealType>& c) |
|
{ // Quantile (or Percent Point) function. |
|
non_central_t_distribution<RealType, Policy> const& dist = c.dist; |
|
RealType q = c.param; |
|
RealType v = dist.degrees_of_freedom(); |
|
RealType l = dist.non_centrality(); |
|
return detail::non_central_t_quantile(v, l, RealType(1-q), q, Policy()); |
|
} // quantile complement. |
|
|
|
} // namespace math |
|
} // namespace boost |
|
|
|
// This include must be at the end, *after* the accessors |
|
// for this distribution have been defined, in order to |
|
// keep compilers that support two-phase lookup happy. |
|
#include <boost/math/distributions/detail/derived_accessors.hpp> |
|
|
|
#endif // BOOST_MATH_SPECIAL_NON_CENTRAL_T_HPP |
|
|
|
|