1 // boost\math\distributions\non_central_chi_squared.hpp 2 3 // Copyright John Maddock 2008. 4 5 // Use, modification and distribution are subject to the 6 // Boost Software License, Version 1.0. 7 // (See accompanying file LICENSE_1_0.txt 8 // or copy at http://www.boost.org/LICENSE_1_0.txt) 9 10 #ifndef BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP 11 #define BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP 12 13 #include <boost/math/distributions/fwd.hpp> 14 #include <boost/math/special_functions/gamma.hpp> // for incomplete gamma. gamma_q 15 #include <boost/math/special_functions/bessel.hpp> // for cyl_bessel_i 16 #include <boost/math/special_functions/round.hpp> // for iround 17 #include <boost/math/distributions/complement.hpp> // complements 18 #include <boost/math/distributions/chi_squared.hpp> // central distribution 19 #include <boost/math/distributions/detail/common_error_handling.hpp> // error checks 20 #include <boost/math/special_functions/fpclassify.hpp> // isnan. 21 #include <boost/math/tools/roots.hpp> // for root finding. 22 #include <boost/math/distributions/detail/generic_mode.hpp> 23 #include <boost/math/distributions/detail/generic_quantile.hpp> 24 25 namespace boost 26 { 27 namespace math 28 { 29 30 template <class RealType, class Policy> 31 class non_central_chi_squared_distribution; 32 33 namespace detail{ 34 35 template <class T, class Policy> 36 T non_central_chi_square_q(T x, T f, T theta, const Policy& pol, T init_sum = 0) 37 { 38 // 39 // Computes the complement of the Non-Central Chi-Square 40 // Distribution CDF by summing a weighted sum of complements 41 // of the central-distributions. The weighting factor is 42 // a Poisson Distribution. 43 // 44 // This is an application of the technique described in: 45 // 46 // Computing discrete mixtures of continuous 47 // distributions: noncentral chisquare, noncentral t 48 // and the distribution of the square of the sample 49 // multiple correlation coeficient. 50 // D. Benton, K. Krishnamoorthy. 51 // Computational Statistics & Data Analysis 43 (2003) 249 - 267 52 // 53 BOOST_MATH_STD_USING 54 55 // Special case: 56 if(x == 0) 57 return 1; 58 59 // 60 // Initialize the variables we'll be using: 61 // 62 T lambda = theta / 2; 63 T del = f / 2; 64 T y = x / 2; 65 boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); 66 T errtol = boost::math::policies::get_epsilon<T, Policy>(); 67 T sum = init_sum; 68 // 69 // k is the starting location for iteration, we'll 70 // move both forwards and backwards from this point. 71 // k is chosen as the peek of the Poisson weights, which 72 // will occur *before* the largest term. 73 // 74 int k = iround(lambda, pol); 75 // Forwards and backwards Poisson weights: 76 T poisf = boost::math::gamma_p_derivative(static_cast<T>(1 + k), lambda, pol); 77 T poisb = poisf * k / lambda; 78 // Initial forwards central chi squared term: 79 T gamf = boost::math::gamma_q(del + k, y, pol); 80 // Forwards and backwards recursion terms on the central chi squared: 81 T xtermf = boost::math::gamma_p_derivative(del + 1 + k, y, pol); 82 T xtermb = xtermf * (del + k) / y; 83 // Initial backwards central chi squared term: 84 T gamb = gamf - xtermb; 85 86 // 87 // Forwards iteration first, this is the 88 // stable direction for the gamma function 89 // recurrences: 90 // 91 int i; 92 for(i = k; static_cast<boost::uintmax_t>(i-k) < max_iter; ++i) 93 { 94 T term = poisf * gamf; 95 sum += term; 96 poisf *= lambda / (i + 1); 97 gamf += xtermf; 98 xtermf *= y / (del + i + 1); 99 if(((sum == 0) || (fabs(term / sum) < errtol)) && (term >= poisf * gamf)) 100 break; 101 } 102 //Error check: 103 if(static_cast<boost::uintmax_t>(i-k) >= max_iter) 104 return policies::raise_evaluation_error( 105 "cdf(non_central_chi_squared_distribution<%1%>, %1%)", 106 "Series did not converge, closest value was %1%", sum, pol); 107 // 108 // Now backwards iteration: the gamma 109 // function recurrences are unstable in this 110 // direction, we rely on the terms deminishing in size 111 // faster than we introduce cancellation errors. 112 // For this reason it's very important that we start 113 // *before* the largest term so that backwards iteration 114 // is strictly converging. 115 // 116 for(i = k - 1; i >= 0; --i) 117 { 118 T term = poisb * gamb; 119 sum += term; 120 poisb *= i / lambda; 121 xtermb *= (del + i) / y; 122 gamb -= xtermb; 123 if((sum == 0) || (fabs(term / sum) < errtol)) 124 break; 125 } 126 127 return sum; 128 } 129 130 template <class T, class Policy> 131 T non_central_chi_square_p_ding(T x, T f, T theta, const Policy& pol, T init_sum = 0) 132 { 133 // 134 // This is an implementation of: 135 // 136 // Algorithm AS 275: 137 // Computing the Non-Central #2 Distribution Function 138 // Cherng G. Ding 139 // Applied Statistics, Vol. 41, No. 2. (1992), pp. 478-482. 140 // 141 // This uses a stable forward iteration to sum the 142 // CDF, unfortunately this can not be used for large 143 // values of the non-centrality parameter because: 144 // * The first term may underfow to zero. 145 // * We may need an extra-ordinary number of terms 146 // before we reach the first *significant* term. 147 // 148 BOOST_MATH_STD_USING 149 // Special case: 150 if(x == 0) 151 return 0; 152 T tk = boost::math::gamma_p_derivative(f/2 + 1, x/2, pol); 153 T lambda = theta / 2; 154 T vk = exp(-lambda); 155 T uk = vk; 156 T sum = init_sum + tk * vk; 157 if(sum == 0) 158 return sum; 159 160 boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); 161 T errtol = boost::math::policies::get_epsilon<T, Policy>(); 162 163 int i; 164 T lterm(0), term(0); 165 for(i = 1; static_cast<boost::uintmax_t>(i) < max_iter; ++i) 166 { 167 tk = tk * x / (f + 2 * i); 168 uk = uk * lambda / i; 169 vk = vk + uk; 170 lterm = term; 171 term = vk * tk; 172 sum += term; 173 if((fabs(term / sum) < errtol) && (term <= lterm)) 174 break; 175 } 176 //Error check: 177 if(static_cast<boost::uintmax_t>(i) >= max_iter) 178 return policies::raise_evaluation_error( 179 "cdf(non_central_chi_squared_distribution<%1%>, %1%)", 180 "Series did not converge, closest value was %1%", sum, pol); 181 return sum; 182 } 183 184 185 template <class T, class Policy> 186 T non_central_chi_square_p(T y, T n, T lambda, const Policy& pol, T init_sum) 187 { 188 // 189 // This is taken more or less directly from: 190 // 191 // Computing discrete mixtures of continuous 192 // distributions: noncentral chisquare, noncentral t 193 // and the distribution of the square of the sample 194 // multiple correlation coeficient. 195 // D. Benton, K. Krishnamoorthy. 196 // Computational Statistics & Data Analysis 43 (2003) 249 - 267 197 // 198 // We're summing a Poisson weighting term multiplied by 199 // a central chi squared distribution. 200 // 201 BOOST_MATH_STD_USING 202 // Special case: 203 if(y == 0) 204 return 0; 205 boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); 206 T errtol = boost::math::policies::get_epsilon<T, Policy>(); 207 T errorf(0), errorb(0); 208 209 T x = y / 2; 210 T del = lambda / 2; 211 // 212 // Starting location for the iteration, we'll iterate 213 // both forwards and backwards from this point. The 214 // location chosen is the maximum of the Poisson weight 215 // function, which ocurrs *after* the largest term in the 216 // sum. 217 // 218 int k = iround(del, pol); 219 T a = n / 2 + k; 220 // Central chi squared term for forward iteration: 221 T gamkf = boost::math::gamma_p(a, x, pol); 222 223 if(lambda == 0) 224 return gamkf; 225 // Central chi squared term for backward iteration: 226 T gamkb = gamkf; 227 // Forwards Poisson weight: 228 T poiskf = gamma_p_derivative(static_cast<T>(k+1), del, pol); 229 // Backwards Poisson weight: 230 T poiskb = poiskf; 231 // Forwards gamma function recursion term: 232 T xtermf = boost::math::gamma_p_derivative(a, x, pol); 233 // Backwards gamma function recursion term: 234 T xtermb = xtermf * x / a; 235 T sum = init_sum + poiskf * gamkf; 236 if(sum == 0) 237 return sum; 238 int i = 1; 239 // 240 // Backwards recursion first, this is the stable 241 // direction for gamma function recurrences: 242 // 243 while(i <= k) 244 { 245 xtermb *= (a - i + 1) / x; 246 gamkb += xtermb; 247 poiskb = poiskb * (k - i + 1) / del; 248 errorf = errorb; 249 errorb = gamkb * poiskb; 250 sum += errorb; 251 if((fabs(errorb / sum) < errtol) && (errorb <= errorf)) 252 break; 253 ++i; 254 } 255 i = 1; 256 // 257 // Now forwards recursion, the gamma function 258 // recurrence relation is unstable in this direction, 259 // so we rely on the magnitude of successive terms 260 // decreasing faster than we introduce cancellation error. 261 // For this reason it's vital that k is chosen to be *after* 262 // the largest term, so that successive forward iterations 263 // are strictly (and rapidly) converging. 264 // 265 do 266 { 267 xtermf = xtermf * x / (a + i - 1); 268 gamkf = gamkf - xtermf; 269 poiskf = poiskf * del / (k + i); 270 errorf = poiskf * gamkf; 271 sum += errorf; 272 ++i; 273 }while((fabs(errorf / sum) > errtol) && (static_cast<boost::uintmax_t>(i) < max_iter)); 274 275 //Error check: 276 if(static_cast<boost::uintmax_t>(i) >= max_iter) 277 return policies::raise_evaluation_error( 278 "cdf(non_central_chi_squared_distribution<%1%>, %1%)", 279 "Series did not converge, closest value was %1%", sum, pol); 280 281 return sum; 282 } 283 284 template <class T, class Policy> 285 T non_central_chi_square_pdf(T x, T n, T lambda, const Policy& pol) 286 { 287 // 288 // As above but for the PDF: 289 // 290 BOOST_MATH_STD_USING 291 boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); 292 T errtol = boost::math::policies::get_epsilon<T, Policy>(); 293 T x2 = x / 2; 294 T n2 = n / 2; 295 T l2 = lambda / 2; 296 T sum = 0; 297 int k = itrunc(l2); 298 T pois = gamma_p_derivative(static_cast<T>(k + 1), l2, pol) * gamma_p_derivative(static_cast<T>(n2 + k), x2); 299 if(pois == 0) 300 return 0; 301 T poisb = pois; 302 for(int i = k; ; ++i) 303 { 304 sum += pois; 305 if(pois / sum < errtol) 306 break; 307 if(static_cast<boost::uintmax_t>(i - k) >= max_iter) 308 return policies::raise_evaluation_error( 309 "pdf(non_central_chi_squared_distribution<%1%>, %1%)", 310 "Series did not converge, closest value was %1%", sum, pol); 311 pois *= l2 * x2 / ((i + 1) * (n2 + i)); 312 } 313 for(int i = k - 1; i >= 0; --i) 314 { 315 poisb *= (i + 1) * (n2 + i) / (l2 * x2); 316 sum += poisb; 317 if(poisb / sum < errtol) 318 break; 319 } 320 return sum / 2; 321 } 322 323 template <class RealType, class Policy> non_central_chi_squared_cdf(RealType x,RealType k,RealType l,bool invert,const Policy &)324 inline RealType non_central_chi_squared_cdf(RealType x, RealType k, RealType l, bool invert, const Policy&) 325 { 326 typedef typename policies::evaluation<RealType, Policy>::type value_type; 327 typedef typename policies::normalise< 328 Policy, 329 policies::promote_float<false>, 330 policies::promote_double<false>, 331 policies::discrete_quantile<>, 332 policies::assert_undefined<> >::type forwarding_policy; 333 334 BOOST_MATH_STD_USING 335 value_type result; 336 if(l == 0) 337 return invert == false ? cdf(boost::math::chi_squared_distribution<RealType, Policy>(k), x) : cdf(complement(boost::math::chi_squared_distribution<RealType, Policy>(k), x)); 338 else if(x > k + l) 339 { 340 // Complement is the smaller of the two: 341 result = detail::non_central_chi_square_q( 342 static_cast<value_type>(x), 343 static_cast<value_type>(k), 344 static_cast<value_type>(l), 345 forwarding_policy(), 346 static_cast<value_type>(invert ? 0 : -1)); 347 invert = !invert; 348 } 349 else if(l < 200) 350 { 351 // For small values of the non-centrality parameter 352 // we can use Ding's method: 353 result = detail::non_central_chi_square_p_ding( 354 static_cast<value_type>(x), 355 static_cast<value_type>(k), 356 static_cast<value_type>(l), 357 forwarding_policy(), 358 static_cast<value_type>(invert ? -1 : 0)); 359 } 360 else 361 { 362 // For largers values of the non-centrality 363 // parameter Ding's method will consume an 364 // extra-ordinary number of terms, and worse 365 // may return zero when the result is in fact 366 // finite, use Krishnamoorthy's method instead: 367 result = detail::non_central_chi_square_p( 368 static_cast<value_type>(x), 369 static_cast<value_type>(k), 370 static_cast<value_type>(l), 371 forwarding_policy(), 372 static_cast<value_type>(invert ? -1 : 0)); 373 } 374 if(invert) 375 result = -result; 376 return policies::checked_narrowing_cast<RealType, forwarding_policy>( 377 result, 378 "boost::math::non_central_chi_squared_cdf<%1%>(%1%, %1%, %1%)"); 379 } 380 381 template <class T, class Policy> 382 struct nccs_quantile_functor 383 { nccs_quantile_functorboost::math::detail::nccs_quantile_functor384 nccs_quantile_functor(const non_central_chi_squared_distribution<T,Policy>& d, T t, bool c) 385 : dist(d), target(t), comp(c) {} 386 operator ()boost::math::detail::nccs_quantile_functor387 T operator()(const T& x) 388 { 389 return comp ? 390 target - cdf(complement(dist, x)) 391 : cdf(dist, x) - target; 392 } 393 394 private: 395 non_central_chi_squared_distribution<T,Policy> dist; 396 T target; 397 bool comp; 398 }; 399 400 template <class RealType, class Policy> 401 RealType nccs_quantile(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& p, bool comp) 402 { 403 BOOST_MATH_STD_USING 404 static const char* function = "quantile(non_central_chi_squared_distribution<%1%>, %1%)"; 405 typedef typename policies::evaluation<RealType, Policy>::type value_type; 406 typedef typename policies::normalise< 407 Policy, 408 policies::promote_float<false>, 409 policies::promote_double<false>, 410 policies::discrete_quantile<>, 411 policies::assert_undefined<> >::type forwarding_policy; 412 413 value_type k = dist.degrees_of_freedom(); 414 value_type l = dist.non_centrality(); 415 value_type r; 416 if(!detail::check_df( 417 function, 418 k, &r, Policy()) 419 || 420 !detail::check_non_centrality( 421 function, 422 l, 423 &r, 424 Policy()) 425 || 426 !detail::check_probability( 427 function, 428 static_cast<value_type>(p), 429 &r, 430 Policy())) 431 return (RealType)r; 432 // 433 // Special cases get short-circuited first: 434 // 435 if(p == 0) 436 return comp ? policies::raise_overflow_error<RealType>(function, 0, Policy()) : 0; 437 if(p == 1) 438 return comp ? 0 : policies::raise_overflow_error<RealType>(function, 0, Policy()); 439 // 440 // This is Pearson's approximation to the quantile, see 441 // Pearson, E. S. (1959) "Note on an approximation to the distribution of 442 // noncentral chi squared", Biometrika 46: 364. 443 // See also: 444 // "A comparison of approximations to percentiles of the noncentral chi2-distribution", 445 // Hardeo Sahai and Mario Miguel Ojeda, Revista de Matematica: Teoria y Aplicaciones 2003 10(1-2) : 57-76. 446 // Note that the latter reference refers to an approximation of the CDF, when they really mean the quantile. 447 // 448 value_type b = -(l * l) / (k + 3 * l); 449 value_type c = (k + 3 * l) / (k + 2 * l); 450 value_type ff = (k + 2 * l) / (c * c); 451 value_type guess; 452 if(comp) 453 { 454 guess = b + c * quantile(complement(chi_squared_distribution<value_type, forwarding_policy>(ff), p)); 455 } 456 else 457 { 458 guess = b + c * quantile(chi_squared_distribution<value_type, forwarding_policy>(ff), p); 459 } 460 // 461 // Sometimes guess goes very small or negative, in that case we have 462 // to do something else for the initial guess, this approximation 463 // was provided in a private communication from Thomas Luu, PhD candidate, 464 // University College London. It's an asymptotic expansion for the 465 // quantile which usually gets us within an order of magnitude of the 466 // correct answer. 467 // Fast and accurate parallel computation of quantile functions for random number generation, 468 // Thomas LuuDoctorial Thesis 2016 469 // http://discovery.ucl.ac.uk/1482128/ 470 // 471 if(guess < 0.005) 472 { 473 value_type pp = comp ? 1 - p : p; 474 //guess = pow(pow(value_type(2), (k / 2 - 1)) * exp(l / 2) * pp * k, 2 / k); 475 guess = pow(pow(value_type(2), (k / 2 - 1)) * exp(l / 2) * pp * k * boost::math::tgamma(k / 2, forwarding_policy()), (2 / k)); 476 if(guess == 0) 477 guess = tools::min_value<value_type>(); 478 } 479 value_type result = detail::generic_quantile( 480 non_central_chi_squared_distribution<value_type, forwarding_policy>(k, l), 481 p, 482 guess, 483 comp, 484 function); 485 486 return policies::checked_narrowing_cast<RealType, forwarding_policy>( 487 result, 488 function); 489 } 490 491 template <class RealType, class Policy> 492 RealType nccs_pdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x) 493 { 494 BOOST_MATH_STD_USING 495 static const char* function = "pdf(non_central_chi_squared_distribution<%1%>, %1%)"; 496 typedef typename policies::evaluation<RealType, Policy>::type value_type; 497 typedef typename policies::normalise< 498 Policy, 499 policies::promote_float<false>, 500 policies::promote_double<false>, 501 policies::discrete_quantile<>, 502 policies::assert_undefined<> >::type forwarding_policy; 503 504 value_type k = dist.degrees_of_freedom(); 505 value_type l = dist.non_centrality(); 506 value_type r; 507 if(!detail::check_df( 508 function, 509 k, &r, Policy()) 510 || 511 !detail::check_non_centrality( 512 function, 513 l, 514 &r, 515 Policy()) 516 || 517 !detail::check_positive_x( 518 function, 519 (value_type)x, 520 &r, 521 Policy())) 522 return (RealType)r; 523 524 if(l == 0) 525 return pdf(boost::math::chi_squared_distribution<RealType, forwarding_policy>(dist.degrees_of_freedom()), x); 526 527 // Special case: 528 if(x == 0) 529 return 0; 530 if(l > 50) 531 { 532 r = non_central_chi_square_pdf(static_cast<value_type>(x), k, l, forwarding_policy()); 533 } 534 else 535 { 536 r = log(x / l) * (k / 4 - 0.5f) - (x + l) / 2; 537 if(fabs(r) >= tools::log_max_value<RealType>() / 4) 538 { 539 r = non_central_chi_square_pdf(static_cast<value_type>(x), k, l, forwarding_policy()); 540 } 541 else 542 { 543 r = exp(r); 544 r = 0.5f * r 545 * boost::math::cyl_bessel_i(k/2 - 1, sqrt(l * x), forwarding_policy()); 546 } 547 } 548 return policies::checked_narrowing_cast<RealType, forwarding_policy>( 549 r, 550 function); 551 } 552 553 template <class RealType, class Policy> 554 struct degrees_of_freedom_finder 555 { degrees_of_freedom_finderboost::math::detail::degrees_of_freedom_finder556 degrees_of_freedom_finder( 557 RealType lam_, RealType x_, RealType p_, bool c) 558 : lam(lam_), x(x_), p(p_), comp(c) {} 559 operator ()boost::math::detail::degrees_of_freedom_finder560 RealType operator()(const RealType& v) 561 { 562 non_central_chi_squared_distribution<RealType, Policy> d(v, lam); 563 return comp ? 564 RealType(p - cdf(complement(d, x))) 565 : RealType(cdf(d, x) - p); 566 } 567 private: 568 RealType lam; 569 RealType x; 570 RealType p; 571 bool comp; 572 }; 573 574 template <class RealType, class Policy> find_degrees_of_freedom(RealType lam,RealType x,RealType p,RealType q,const Policy & pol)575 inline RealType find_degrees_of_freedom( 576 RealType lam, RealType x, RealType p, RealType q, const Policy& pol) 577 { 578 const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom"; 579 if((p == 0) || (q == 0)) 580 { 581 // 582 // Can't a thing if one of p and q is zero: 583 // 584 return policies::raise_evaluation_error<RealType>(function, 585 "Can't find degrees of freedom when the probability is 0 or 1, only possible answer is %1%", 586 RealType(std::numeric_limits<RealType>::quiet_NaN()), Policy()); 587 } 588 degrees_of_freedom_finder<RealType, Policy> f(lam, x, p < q ? p : q, p < q ? false : true); 589 tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>()); 590 boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>(); 591 // 592 // Pick an initial guess that we know will give us a probability 593 // right around 0.5. 594 // 595 RealType guess = x - lam; 596 if(guess < 1) 597 guess = 1; 598 std::pair<RealType, RealType> ir = tools::bracket_and_solve_root( 599 f, guess, RealType(2), false, tol, max_iter, pol); 600 RealType result = ir.first + (ir.second - ir.first) / 2; 601 if(max_iter >= policies::get_max_root_iterations<Policy>()) 602 { 603 return policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:" 604 " or there is no answer to problem. Current best guess is %1%", result, Policy()); 605 } 606 return result; 607 } 608 609 template <class RealType, class Policy> 610 struct non_centrality_finder 611 { non_centrality_finderboost::math::detail::non_centrality_finder612 non_centrality_finder( 613 RealType v_, RealType x_, RealType p_, bool c) 614 : v(v_), x(x_), p(p_), comp(c) {} 615 operator ()boost::math::detail::non_centrality_finder616 RealType operator()(const RealType& lam) 617 { 618 non_central_chi_squared_distribution<RealType, Policy> d(v, lam); 619 return comp ? 620 RealType(p - cdf(complement(d, x))) 621 : RealType(cdf(d, x) - p); 622 } 623 private: 624 RealType v; 625 RealType x; 626 RealType p; 627 bool comp; 628 }; 629 630 template <class RealType, class Policy> find_non_centrality(RealType v,RealType x,RealType p,RealType q,const Policy & pol)631 inline RealType find_non_centrality( 632 RealType v, RealType x, RealType p, RealType q, const Policy& pol) 633 { 634 const char* function = "non_central_chi_squared<%1%>::find_non_centrality"; 635 if((p == 0) || (q == 0)) 636 { 637 // 638 // Can't do a thing if one of p and q is zero: 639 // 640 return policies::raise_evaluation_error<RealType>(function, 641 "Can't find non centrality parameter when the probability is 0 or 1, only possible answer is %1%", 642 RealType(std::numeric_limits<RealType>::quiet_NaN()), Policy()); 643 } 644 non_centrality_finder<RealType, Policy> f(v, x, p < q ? p : q, p < q ? false : true); 645 tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>()); 646 boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>(); 647 // 648 // Pick an initial guess that we know will give us a probability 649 // right around 0.5. 650 // 651 RealType guess = x - v; 652 if(guess < 1) 653 guess = 1; 654 std::pair<RealType, RealType> ir = tools::bracket_and_solve_root( 655 f, guess, RealType(2), false, tol, max_iter, pol); 656 RealType result = ir.first + (ir.second - ir.first) / 2; 657 if(max_iter >= policies::get_max_root_iterations<Policy>()) 658 { 659 return policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:" 660 " or there is no answer to problem. Current best guess is %1%", result, Policy()); 661 } 662 return result; 663 } 664 665 } 666 667 template <class RealType = double, class Policy = policies::policy<> > 668 class non_central_chi_squared_distribution 669 { 670 public: 671 typedef RealType value_type; 672 typedef Policy policy_type; 673 non_central_chi_squared_distribution(RealType df_,RealType lambda)674 non_central_chi_squared_distribution(RealType df_, RealType lambda) : df(df_), ncp(lambda) 675 { 676 const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::non_central_chi_squared_distribution(%1%,%1%)"; 677 RealType r; 678 detail::check_df( 679 function, 680 df, &r, Policy()); 681 detail::check_non_centrality( 682 function, 683 ncp, 684 &r, 685 Policy()); 686 } // non_central_chi_squared_distribution constructor. 687 degrees_of_freedom() const688 RealType degrees_of_freedom() const 689 { // Private data getter function. 690 return df; 691 } non_centrality() const692 RealType non_centrality() const 693 { // Private data getter function. 694 return ncp; 695 } find_degrees_of_freedom(RealType lam,RealType x,RealType p)696 static RealType find_degrees_of_freedom(RealType lam, RealType x, RealType p) 697 { 698 const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom"; 699 typedef typename policies::evaluation<RealType, Policy>::type eval_type; 700 typedef typename policies::normalise< 701 Policy, 702 policies::promote_float<false>, 703 policies::promote_double<false>, 704 policies::discrete_quantile<>, 705 policies::assert_undefined<> >::type forwarding_policy; 706 eval_type result = detail::find_degrees_of_freedom( 707 static_cast<eval_type>(lam), 708 static_cast<eval_type>(x), 709 static_cast<eval_type>(p), 710 static_cast<eval_type>(1-p), 711 forwarding_policy()); 712 return policies::checked_narrowing_cast<RealType, forwarding_policy>( 713 result, 714 function); 715 } 716 template <class A, class B, class C> find_degrees_of_freedom(const complemented3_type<A,B,C> & c)717 static RealType find_degrees_of_freedom(const complemented3_type<A,B,C>& c) 718 { 719 const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom"; 720 typedef typename policies::evaluation<RealType, Policy>::type eval_type; 721 typedef typename policies::normalise< 722 Policy, 723 policies::promote_float<false>, 724 policies::promote_double<false>, 725 policies::discrete_quantile<>, 726 policies::assert_undefined<> >::type forwarding_policy; 727 eval_type result = detail::find_degrees_of_freedom( 728 static_cast<eval_type>(c.dist), 729 static_cast<eval_type>(c.param1), 730 static_cast<eval_type>(1-c.param2), 731 static_cast<eval_type>(c.param2), 732 forwarding_policy()); 733 return policies::checked_narrowing_cast<RealType, forwarding_policy>( 734 result, 735 function); 736 } find_non_centrality(RealType v,RealType x,RealType p)737 static RealType find_non_centrality(RealType v, RealType x, RealType p) 738 { 739 const char* function = "non_central_chi_squared<%1%>::find_non_centrality"; 740 typedef typename policies::evaluation<RealType, Policy>::type eval_type; 741 typedef typename policies::normalise< 742 Policy, 743 policies::promote_float<false>, 744 policies::promote_double<false>, 745 policies::discrete_quantile<>, 746 policies::assert_undefined<> >::type forwarding_policy; 747 eval_type result = detail::find_non_centrality( 748 static_cast<eval_type>(v), 749 static_cast<eval_type>(x), 750 static_cast<eval_type>(p), 751 static_cast<eval_type>(1-p), 752 forwarding_policy()); 753 return policies::checked_narrowing_cast<RealType, forwarding_policy>( 754 result, 755 function); 756 } 757 template <class A, class B, class C> find_non_centrality(const complemented3_type<A,B,C> & c)758 static RealType find_non_centrality(const complemented3_type<A,B,C>& c) 759 { 760 const char* function = "non_central_chi_squared<%1%>::find_non_centrality"; 761 typedef typename policies::evaluation<RealType, Policy>::type eval_type; 762 typedef typename policies::normalise< 763 Policy, 764 policies::promote_float<false>, 765 policies::promote_double<false>, 766 policies::discrete_quantile<>, 767 policies::assert_undefined<> >::type forwarding_policy; 768 eval_type result = detail::find_non_centrality( 769 static_cast<eval_type>(c.dist), 770 static_cast<eval_type>(c.param1), 771 static_cast<eval_type>(1-c.param2), 772 static_cast<eval_type>(c.param2), 773 forwarding_policy()); 774 return policies::checked_narrowing_cast<RealType, forwarding_policy>( 775 result, 776 function); 777 } 778 private: 779 // Data member, initialized by constructor. 780 RealType df; // degrees of freedom. 781 RealType ncp; // non-centrality parameter 782 }; // template <class RealType, class Policy> class non_central_chi_squared_distribution 783 784 typedef non_central_chi_squared_distribution<double> non_central_chi_squared; // Reserved name of type double. 785 786 // Non-member functions to give properties of the distribution. 787 788 template <class RealType, class Policy> range(const non_central_chi_squared_distribution<RealType,Policy> &)789 inline const std::pair<RealType, RealType> range(const non_central_chi_squared_distribution<RealType, Policy>& /* dist */) 790 { // Range of permissible values for random variable k. 791 using boost::math::tools::max_value; 792 return std::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); // Max integer? 793 } 794 795 template <class RealType, class Policy> support(const non_central_chi_squared_distribution<RealType,Policy> &)796 inline const std::pair<RealType, RealType> support(const non_central_chi_squared_distribution<RealType, Policy>& /* dist */) 797 { // Range of supported values for random variable k. 798 // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. 799 using boost::math::tools::max_value; 800 return std::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); 801 } 802 803 template <class RealType, class Policy> mean(const non_central_chi_squared_distribution<RealType,Policy> & dist)804 inline RealType mean(const non_central_chi_squared_distribution<RealType, Policy>& dist) 805 { // Mean of poisson distribution = lambda. 806 const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::mean()"; 807 RealType k = dist.degrees_of_freedom(); 808 RealType l = dist.non_centrality(); 809 RealType r; 810 if(!detail::check_df( 811 function, 812 k, &r, Policy()) 813 || 814 !detail::check_non_centrality( 815 function, 816 l, 817 &r, 818 Policy())) 819 return r; 820 return k + l; 821 } // mean 822 823 template <class RealType, class Policy> mode(const non_central_chi_squared_distribution<RealType,Policy> & dist)824 inline RealType mode(const non_central_chi_squared_distribution<RealType, Policy>& dist) 825 { // mode. 826 static const char* function = "mode(non_central_chi_squared_distribution<%1%> const&)"; 827 828 RealType k = dist.degrees_of_freedom(); 829 RealType l = dist.non_centrality(); 830 RealType r; 831 if(!detail::check_df( 832 function, 833 k, &r, Policy()) 834 || 835 !detail::check_non_centrality( 836 function, 837 l, 838 &r, 839 Policy())) 840 return (RealType)r; 841 return detail::generic_find_mode(dist, 1 + k, function); 842 } 843 844 template <class RealType, class Policy> variance(const non_central_chi_squared_distribution<RealType,Policy> & dist)845 inline RealType variance(const non_central_chi_squared_distribution<RealType, Policy>& dist) 846 { // variance. 847 const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::variance()"; 848 RealType k = dist.degrees_of_freedom(); 849 RealType l = dist.non_centrality(); 850 RealType r; 851 if(!detail::check_df( 852 function, 853 k, &r, Policy()) 854 || 855 !detail::check_non_centrality( 856 function, 857 l, 858 &r, 859 Policy())) 860 return r; 861 return 2 * (2 * l + k); 862 } 863 864 // RealType standard_deviation(const non_central_chi_squared_distribution<RealType, Policy>& dist) 865 // standard_deviation provided by derived accessors. 866 867 template <class RealType, class Policy> skewness(const non_central_chi_squared_distribution<RealType,Policy> & dist)868 inline RealType skewness(const non_central_chi_squared_distribution<RealType, Policy>& dist) 869 { // skewness = sqrt(l). 870 const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::skewness()"; 871 RealType k = dist.degrees_of_freedom(); 872 RealType l = dist.non_centrality(); 873 RealType r; 874 if(!detail::check_df( 875 function, 876 k, &r, Policy()) 877 || 878 !detail::check_non_centrality( 879 function, 880 l, 881 &r, 882 Policy())) 883 return r; 884 BOOST_MATH_STD_USING 885 return pow(2 / (k + 2 * l), RealType(3)/2) * (k + 3 * l); 886 } 887 888 template <class RealType, class Policy> kurtosis_excess(const non_central_chi_squared_distribution<RealType,Policy> & dist)889 inline RealType kurtosis_excess(const non_central_chi_squared_distribution<RealType, Policy>& dist) 890 { 891 const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::kurtosis_excess()"; 892 RealType k = dist.degrees_of_freedom(); 893 RealType l = dist.non_centrality(); 894 RealType r; 895 if(!detail::check_df( 896 function, 897 k, &r, Policy()) 898 || 899 !detail::check_non_centrality( 900 function, 901 l, 902 &r, 903 Policy())) 904 return r; 905 return 12 * (k + 4 * l) / ((k + 2 * l) * (k + 2 * l)); 906 } // kurtosis_excess 907 908 template <class RealType, class Policy> kurtosis(const non_central_chi_squared_distribution<RealType,Policy> & dist)909 inline RealType kurtosis(const non_central_chi_squared_distribution<RealType, Policy>& dist) 910 { 911 return kurtosis_excess(dist) + 3; 912 } 913 914 template <class RealType, class Policy> pdf(const non_central_chi_squared_distribution<RealType,Policy> & dist,const RealType & x)915 inline RealType pdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x) 916 { // Probability Density/Mass Function. 917 return detail::nccs_pdf(dist, x); 918 } // pdf 919 920 template <class RealType, class Policy> 921 RealType cdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x) 922 { 923 const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::cdf(%1%)"; 924 RealType k = dist.degrees_of_freedom(); 925 RealType l = dist.non_centrality(); 926 RealType r; 927 if(!detail::check_df( 928 function, 929 k, &r, Policy()) 930 || 931 !detail::check_non_centrality( 932 function, 933 l, 934 &r, 935 Policy()) 936 || 937 !detail::check_positive_x( 938 function, 939 x, 940 &r, 941 Policy())) 942 return r; 943 944 return detail::non_central_chi_squared_cdf(x, k, l, false, Policy()); 945 } // cdf 946 947 template <class RealType, class Policy> 948 RealType cdf(const complemented2_type<non_central_chi_squared_distribution<RealType, Policy>, RealType>& c) 949 { // Complemented Cumulative Distribution Function 950 const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::cdf(%1%)"; 951 non_central_chi_squared_distribution<RealType, Policy> const& dist = c.dist; 952 RealType x = c.param; 953 RealType k = dist.degrees_of_freedom(); 954 RealType l = dist.non_centrality(); 955 RealType r; 956 if(!detail::check_df( 957 function, 958 k, &r, Policy()) 959 || 960 !detail::check_non_centrality( 961 function, 962 l, 963 &r, 964 Policy()) 965 || 966 !detail::check_positive_x( 967 function, 968 x, 969 &r, 970 Policy())) 971 return r; 972 973 return detail::non_central_chi_squared_cdf(x, k, l, true, Policy()); 974 } // ccdf 975 976 template <class RealType, class Policy> quantile(const non_central_chi_squared_distribution<RealType,Policy> & dist,const RealType & p)977 inline RealType quantile(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& p) 978 { // Quantile (or Percent Point) function. 979 return detail::nccs_quantile(dist, p, false); 980 } // quantile 981 982 template <class RealType, class Policy> quantile(const complemented2_type<non_central_chi_squared_distribution<RealType,Policy>,RealType> & c)983 inline RealType quantile(const complemented2_type<non_central_chi_squared_distribution<RealType, Policy>, RealType>& c) 984 { // Quantile (or Percent Point) function. 985 return detail::nccs_quantile(c.dist, c.param, true); 986 } // quantile complement. 987 988 } // namespace math 989 } // namespace boost 990 991 // This include must be at the end, *after* the accessors 992 // for this distribution have been defined, in order to 993 // keep compilers that support two-phase lookup happy. 994 #include <boost/math/distributions/detail/derived_accessors.hpp> 995 996 #endif // BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP 997 998 999 1000