test_nc_chi_squared.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. // (C) Copyright John Maddock 2007.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error
  6. #include <boost/math/concepts/real_concept.hpp>
  7. #define BOOST_TEST_MAIN
  8. #include <boost/test/unit_test.hpp>
  9. #include <boost/test/tools/floating_point_comparison.hpp>
  10. #include <boost/math/distributions/non_central_chi_squared.hpp>
  11. #include <boost/type_traits/is_floating_point.hpp>
  12. #include <boost/array.hpp>
  13. #include "functor.hpp"
  14. #include "handle_test_result.hpp"
  15. #include "table_type.hpp"
  16. #include <iostream>
  17. #include <iomanip>
  18. #define BOOST_CHECK_CLOSE_EX(a, b, prec, i) \
  19. {\
  20. unsigned int failures = boost::unit_test::results_collector.results( boost::unit_test::framework::current_test_case().p_id ).p_assertions_failed;\
  21. BOOST_CHECK_CLOSE(a, b, prec); \
  22. if(failures != boost::unit_test::results_collector.results( boost::unit_test::framework::current_test_case().p_id ).p_assertions_failed)\
  23. {\
  24. std::cerr << "Failure was at row " << i << std::endl;\
  25. std::cerr << std::setprecision(35); \
  26. std::cerr << "{ " << data[i][0] << " , " << data[i][1] << " , " << data[i][2];\
  27. std::cerr << " , " << data[i][3] << " , " << data[i][4] << " } " << std::endl;\
  28. }\
  29. }
  30. #define BOOST_CHECK_EX(a, i) \
  31. {\
  32. unsigned int failures = boost::unit_test::results_collector.results( boost::unit_test::framework::current_test_case().p_id ).p_assertions_failed;\
  33. BOOST_CHECK(a); \
  34. if(failures != boost::unit_test::results_collector.results( boost::unit_test::framework::current_test_case().p_id ).p_assertions_failed)\
  35. {\
  36. std::cerr << "Failure was at row " << i << std::endl;\
  37. std::cerr << std::setprecision(35); \
  38. std::cerr << "{ " << data[i][0] << " , " << data[i][1] << " , " << data[i][2];\
  39. std::cerr << " , " << data[i][3] << " , " << data[i][4] << " } " << std::endl;\
  40. }\
  41. }
  42. template <class RealType>
  43. RealType naive_pdf(RealType v, RealType lam, RealType x)
  44. {
  45. // Formula direct from
  46. // http://mathworld.wolfram.com/NoncentralChi-SquaredDistribution.html
  47. // with no simplification:
  48. RealType sum, term, prefix(1);
  49. RealType eps = boost::math::tools::epsilon<RealType>();
  50. term = sum = pdf(boost::math::chi_squared_distribution<RealType>(v), x);
  51. for(int i = 1;; ++i)
  52. {
  53. prefix *= lam / (2 * i);
  54. term = prefix * pdf(boost::math::chi_squared_distribution<RealType>(v + 2 * i), x);
  55. sum += term;
  56. if(term / sum < eps)
  57. break;
  58. }
  59. return sum * exp(-lam / 2);
  60. }
  61. template <class RealType>
  62. void test_spot(
  63. RealType df, // Degrees of freedom
  64. RealType ncp, // non-centrality param
  65. RealType cs, // Chi Square statistic
  66. RealType P, // CDF
  67. RealType Q, // Complement of CDF
  68. RealType tol) // Test tolerance
  69. {
  70. boost::math::non_central_chi_squared_distribution<RealType> dist(df, ncp);
  71. BOOST_CHECK_CLOSE(
  72. cdf(dist, cs), P, tol);
  73. #ifndef BOOST_NO_EXCEPTIONS
  74. try{
  75. BOOST_CHECK_CLOSE(
  76. pdf(dist, cs), naive_pdf(dist.degrees_of_freedom(), ncp, cs), tol * 150);
  77. }
  78. catch(const std::overflow_error&)
  79. {
  80. }
  81. #endif
  82. if((P < 0.99) && (Q < 0.99))
  83. {
  84. //
  85. // We can only check this if P is not too close to 1,
  86. // so that we can guarantee Q is reasonably free of error:
  87. //
  88. BOOST_CHECK_CLOSE(
  89. cdf(complement(dist, cs)), Q, tol);
  90. BOOST_CHECK_CLOSE(
  91. quantile(dist, P), cs, tol * 10);
  92. BOOST_CHECK_CLOSE(
  93. quantile(complement(dist, Q)), cs, tol * 10);
  94. BOOST_CHECK_CLOSE(
  95. dist.find_degrees_of_freedom(ncp, cs, P), df, tol * 10);
  96. BOOST_CHECK_CLOSE(
  97. dist.find_degrees_of_freedom(boost::math::complement(ncp, cs, Q)), df, tol * 10);
  98. BOOST_CHECK_CLOSE(
  99. dist.find_non_centrality(df, cs, P), ncp, tol * 10);
  100. BOOST_CHECK_CLOSE(
  101. dist.find_non_centrality(boost::math::complement(df, cs, Q)), ncp, tol * 10);
  102. }
  103. }
  104. template <class RealType> // Any floating-point type RealType.
  105. void test_spots(RealType)
  106. {
  107. #ifndef ERROR_REPORTING_MODE
  108. RealType tolerance = (std::max)(
  109. boost::math::tools::epsilon<RealType>(),
  110. (RealType)boost::math::tools::epsilon<double>() * 5) * 150;
  111. //
  112. // At float precision we need to up the tolerance, since
  113. // the input values are rounded off to inexact quantities
  114. // the results get thrown off by a noticeable amount.
  115. //
  116. if(boost::math::tools::digits<RealType>() < 50)
  117. tolerance *= 50;
  118. if(boost::is_floating_point<RealType>::value != 1)
  119. tolerance *= 20; // real_concept special functions are less accurate
  120. std::cout << "Tolerance = " << tolerance << "%." << std::endl;
  121. using boost::math::chi_squared_distribution;
  122. using ::boost::math::chi_squared;
  123. using ::boost::math::cdf;
  124. using ::boost::math::pdf;
  125. //
  126. // Test against the data from Table 6 of:
  127. //
  128. // "Self-Validating Computations of Probabilities for Selected
  129. // Central and Noncentral Univariate Probability Functions."
  130. // Morgan C. Wang; William J. Kennedy
  131. // Journal of the American Statistical Association,
  132. // Vol. 89, No. 427. (Sep., 1994), pp. 878-887.
  133. //
  134. test_spot(
  135. static_cast<RealType>(1), // degrees of freedom
  136. static_cast<RealType>(6), // non centrality
  137. static_cast<RealType>(0.00393), // Chi Squared statistic
  138. static_cast<RealType>(0.2498463724258039e-2), // Probability of result (CDF), P
  139. static_cast<RealType>(1 - 0.2498463724258039e-2), // Q = 1 - P
  140. tolerance);
  141. test_spot(
  142. static_cast<RealType>(5), // degrees of freedom
  143. static_cast<RealType>(1), // non centrality
  144. static_cast<RealType>(9.23636), // Chi Squared statistic
  145. static_cast<RealType>(0.8272918751175548), // Probability of result (CDF), P
  146. static_cast<RealType>(1 - 0.8272918751175548), // Q = 1 - P
  147. tolerance);
  148. test_spot(
  149. static_cast<RealType>(11), // degrees of freedom
  150. static_cast<RealType>(21), // non centrality
  151. static_cast<RealType>(24.72497), // Chi Squared statistic
  152. static_cast<RealType>(0.2539481822183126), // Probability of result (CDF), P
  153. static_cast<RealType>(1 - 0.2539481822183126), // Q = 1 - P
  154. tolerance);
  155. test_spot(
  156. static_cast<RealType>(31), // degrees of freedom
  157. static_cast<RealType>(6), // non centrality
  158. static_cast<RealType>(44.98534), // Chi Squared statistic
  159. static_cast<RealType>(0.8125198785064969), // Probability of result (CDF), P
  160. static_cast<RealType>(1 - 0.8125198785064969), // Q = 1 - P
  161. tolerance);
  162. test_spot(
  163. static_cast<RealType>(51), // degrees of freedom
  164. static_cast<RealType>(1), // non centrality
  165. static_cast<RealType>(38.56038), // Chi Squared statistic
  166. static_cast<RealType>(0.8519497361859118e-1), // Probability of result (CDF), P
  167. static_cast<RealType>(1 - 0.8519497361859118e-1), // Q = 1 - P
  168. tolerance * 2);
  169. test_spot(
  170. static_cast<RealType>(100), // degrees of freedom
  171. static_cast<RealType>(16), // non centrality
  172. static_cast<RealType>(82.35814), // Chi Squared statistic
  173. static_cast<RealType>(0.1184348822747824e-1), // Probability of result (CDF), P
  174. static_cast<RealType>(1 - 0.1184348822747824e-1), // Q = 1 - P
  175. tolerance);
  176. test_spot(
  177. static_cast<RealType>(300), // degrees of freedom
  178. static_cast<RealType>(16), // non centrality
  179. static_cast<RealType>(331.78852), // Chi Squared statistic
  180. static_cast<RealType>(0.7355956710306709), // Probability of result (CDF), P
  181. static_cast<RealType>(1 - 0.7355956710306709), // Q = 1 - P
  182. tolerance);
  183. test_spot(
  184. static_cast<RealType>(500), // degrees of freedom
  185. static_cast<RealType>(21), // non centrality
  186. static_cast<RealType>(459.92612), // Chi Squared statistic
  187. static_cast<RealType>(0.2797023600800060e-1), // Probability of result (CDF), P
  188. static_cast<RealType>(1 - 0.2797023600800060e-1), // Q = 1 - P
  189. tolerance);
  190. test_spot(
  191. static_cast<RealType>(1), // degrees of freedom
  192. static_cast<RealType>(1), // non centrality
  193. static_cast<RealType>(0.00016), // Chi Squared statistic
  194. static_cast<RealType>(0.6121428929881423e-2), // Probability of result (CDF), P
  195. static_cast<RealType>(1 - 0.6121428929881423e-2), // Q = 1 - P
  196. tolerance);
  197. test_spot(
  198. static_cast<RealType>(1), // degrees of freedom
  199. static_cast<RealType>(1), // non centrality
  200. static_cast<RealType>(0.00393), // Chi Squared statistic
  201. static_cast<RealType>(0.3033814229753780e-1), // Probability of result (CDF), P
  202. static_cast<RealType>(1 - 0.3033814229753780e-1), // Q = 1 - P
  203. tolerance);
  204. RealType tol2 = boost::math::tools::epsilon<RealType>() * 5 * 100; // 5 eps as a percentage
  205. boost::math::non_central_chi_squared_distribution<RealType> dist(static_cast<RealType>(8), static_cast<RealType>(12));
  206. RealType x = 7;
  207. using namespace std; // ADL of std names.
  208. // mean:
  209. BOOST_CHECK_CLOSE(
  210. mean(dist)
  211. , static_cast<RealType>(8 + 12), tol2);
  212. // variance:
  213. BOOST_CHECK_CLOSE(
  214. variance(dist)
  215. , static_cast<RealType>(64), tol2);
  216. // std deviation:
  217. BOOST_CHECK_CLOSE(
  218. standard_deviation(dist)
  219. , static_cast<RealType>(8), tol2);
  220. // hazard:
  221. BOOST_CHECK_CLOSE(
  222. hazard(dist, x)
  223. , pdf(dist, x) / cdf(complement(dist, x)), tol2);
  224. // cumulative hazard:
  225. BOOST_CHECK_CLOSE(
  226. chf(dist, x)
  227. , -log(cdf(complement(dist, x))), tol2);
  228. // coefficient_of_variation:
  229. BOOST_CHECK_CLOSE(
  230. coefficient_of_variation(dist)
  231. , standard_deviation(dist) / mean(dist), tol2);
  232. // mode:
  233. BOOST_CHECK_CLOSE(
  234. mode(dist)
  235. , static_cast<RealType>(17.184201184730857030170788677340294070728990862663L), sqrt(tolerance * 500));
  236. BOOST_CHECK_CLOSE(
  237. median(dist),
  238. quantile(
  239. boost::math::non_central_chi_squared_distribution<RealType>(
  240. static_cast<RealType>(8),
  241. static_cast<RealType>(12)),
  242. static_cast<RealType>(0.5)), static_cast<RealType>(tol2));
  243. // skewness:
  244. BOOST_CHECK_CLOSE(
  245. skewness(dist)
  246. , static_cast<RealType>(0.6875), tol2);
  247. // kurtosis:
  248. BOOST_CHECK_CLOSE(
  249. kurtosis(dist)
  250. , static_cast<RealType>(3.65625), tol2);
  251. // kurtosis excess:
  252. BOOST_CHECK_CLOSE(
  253. kurtosis_excess(dist)
  254. , static_cast<RealType>(0.65625), tol2);
  255. // Error handling checks:
  256. check_out_of_range<boost::math::non_central_chi_squared_distribution<RealType> >(1, 1);
  257. BOOST_MATH_CHECK_THROW(pdf(boost::math::non_central_chi_squared_distribution<RealType>(0, 1), 0), std::domain_error);
  258. BOOST_MATH_CHECK_THROW(pdf(boost::math::non_central_chi_squared_distribution<RealType>(-1, 1), 0), std::domain_error);
  259. BOOST_MATH_CHECK_THROW(pdf(boost::math::non_central_chi_squared_distribution<RealType>(1, -1), 0), std::domain_error);
  260. BOOST_MATH_CHECK_THROW(quantile(boost::math::non_central_chi_squared_distribution<RealType>(1, 1), -1), std::domain_error);
  261. BOOST_MATH_CHECK_THROW(quantile(boost::math::non_central_chi_squared_distribution<RealType>(1, 1), 2), std::domain_error);
  262. #endif
  263. } // template <class RealType>void test_spots(RealType)
  264. template <class T>
  265. T nccs_cdf(T df, T nc, T x)
  266. {
  267. return cdf(boost::math::non_central_chi_squared_distribution<T>(df, nc), x);
  268. }
  269. template <class T>
  270. T nccs_ccdf(T df, T nc, T x)
  271. {
  272. return cdf(complement(boost::math::non_central_chi_squared_distribution<T>(df, nc), x));
  273. }
  274. template <typename Real, typename T>
  275. void do_test_nc_chi_squared(T& data, const char* type_name, const char* test)
  276. {
  277. typedef Real value_type;
  278. std::cout << "Testing: " << test << std::endl;
  279. #ifdef NC_CHI_SQUARED_CDF_FUNCTION_TO_TEST
  280. value_type(*fp1)(value_type, value_type, value_type) = NC_CHI_SQUARED_CDF_FUNCTION_TO_TEST;
  281. #else
  282. value_type(*fp1)(value_type, value_type, value_type) = nccs_cdf;
  283. #endif
  284. boost::math::tools::test_result<value_type> result;
  285. #if !(defined(ERROR_REPORTING_MODE) && !defined(NC_CHI_SQUARED_CDF_FUNCTION_TO_TEST))
  286. result = boost::math::tools::test_hetero<Real>(
  287. data,
  288. bind_func<Real>(fp1, 0, 1, 2),
  289. extract_result<Real>(3));
  290. handle_test_result(result, data[result.worst()], result.worst(),
  291. type_name, "non central chi squared CDF", test);
  292. #endif
  293. #if !(defined(ERROR_REPORTING_MODE) && !defined(NC_CHI_SQUARED_CCDF_FUNCTION_TO_TEST))
  294. #ifdef NC_CHI_SQUARED_CCDF_FUNCTION_TO_TEST
  295. fp1 = NC_CHI_SQUARED_CCDF_FUNCTION_TO_TEST;
  296. #else
  297. fp1 = nccs_ccdf;
  298. #endif
  299. result = boost::math::tools::test_hetero<Real>(
  300. data,
  301. bind_func<Real>(fp1, 0, 1, 2),
  302. extract_result<Real>(4));
  303. handle_test_result(result, data[result.worst()], result.worst(),
  304. type_name, "non central chi squared CDF complement", test);
  305. std::cout << std::endl;
  306. #endif
  307. }
  308. template <typename Real, typename T>
  309. void quantile_sanity_check(T& data, const char* type_name, const char* test)
  310. {
  311. #ifndef ERROR_REPORTING_MODE
  312. typedef Real value_type;
  313. //
  314. // Tests with type real_concept take rather too long to run, so
  315. // for now we'll disable them:
  316. //
  317. if(!boost::is_floating_point<value_type>::value)
  318. return;
  319. std::cout << "Testing: " << type_name << " quantile sanity check, with tests " << test << std::endl;
  320. //
  321. // These sanity checks test for a round trip accuracy of one half
  322. // of the bits in T, unless T is type float, in which case we check
  323. // for just one decimal digit. The problem here is the sensitivity
  324. // of the functions, not their accuracy. This test data was generated
  325. // for the forward functions, which means that when it is used as
  326. // the input to the inverses then it is necessarily inexact. This rounding
  327. // of the input is what makes the data unsuitable for use as an accuracy check,
  328. // and also demonstrates that you can't in general round-trip these functions.
  329. // It is however a useful sanity check.
  330. //
  331. value_type precision = static_cast<value_type>(ldexp(1.0, 1 - boost::math::policies::digits<value_type, boost::math::policies::policy<> >() / 2)) * 100;
  332. if(boost::math::policies::digits<value_type, boost::math::policies::policy<> >() < 50)
  333. precision = 1; // 1% or two decimal digits, all we can hope for when the input is truncated to float
  334. for(unsigned i = 0; i < data.size(); ++i)
  335. {
  336. if(Real(data[i][3]) == 0)
  337. {
  338. BOOST_CHECK(0 == quantile(boost::math::non_central_chi_squared_distribution<value_type>(data[i][0], data[i][1]), data[i][3]));
  339. }
  340. else if(data[i][3] < 0.9999f)
  341. {
  342. value_type p = quantile(boost::math::non_central_chi_squared_distribution<value_type>(data[i][0], data[i][1]), data[i][3]);
  343. value_type pt = data[i][2];
  344. BOOST_CHECK_CLOSE_EX(pt, p, precision, i);
  345. }
  346. if(data[i][4] == 0)
  347. {
  348. BOOST_CHECK(0 == quantile(complement(boost::math::non_central_chi_squared_distribution<value_type>(data[i][0], data[i][1]), data[i][3])));
  349. }
  350. else if(data[i][4] < 0.9999f)
  351. {
  352. value_type p = quantile(complement(boost::math::non_central_chi_squared_distribution<value_type>(data[i][0], data[i][1]), data[i][4]));
  353. value_type pt = data[i][2];
  354. BOOST_CHECK_CLOSE_EX(pt, p, precision, i);
  355. }
  356. if(boost::math::tools::digits<value_type>() > 50)
  357. {
  358. //
  359. // Sanity check mode, the accuracy of
  360. // the mode is at *best* the square root of the accuracy of the PDF:
  361. //
  362. #ifndef BOOST_NO_EXCEPTIONS
  363. try{
  364. value_type m = mode(boost::math::non_central_chi_squared_distribution<value_type>(data[i][0], data[i][1]));
  365. value_type p = pdf(boost::math::non_central_chi_squared_distribution<value_type>(data[i][0], data[i][1]), m);
  366. BOOST_CHECK_EX(pdf(boost::math::non_central_chi_squared_distribution<value_type>(data[i][0], data[i][1]), m * (1 + sqrt(precision) * 50)) <= p, i);
  367. BOOST_CHECK_EX(pdf(boost::math::non_central_chi_squared_distribution<value_type>(data[i][0], data[i][1]), m * (1 - sqrt(precision)) * 50) <= p, i);
  368. }
  369. catch(const boost::math::evaluation_error&) {}
  370. #endif
  371. //
  372. // Sanity check degrees-of-freedom finder, don't bother at float
  373. // precision though as there's not enough data in the probability
  374. // values to get back to the correct degrees of freedom or
  375. // non-cenrality parameter:
  376. //
  377. #ifndef BOOST_NO_EXCEPTIONS
  378. try{
  379. #endif
  380. if((data[i][3] < 0.99) && (data[i][3] != 0))
  381. {
  382. BOOST_CHECK_CLOSE_EX(
  383. boost::math::non_central_chi_squared_distribution<value_type>::find_degrees_of_freedom(data[i][1], data[i][2], data[i][3]),
  384. data[i][0], precision, i);
  385. BOOST_CHECK_CLOSE_EX(
  386. boost::math::non_central_chi_squared_distribution<value_type>::find_non_centrality(data[i][0], data[i][2], data[i][3]),
  387. data[i][1], precision, i);
  388. }
  389. if((data[i][4] < 0.99) && (data[i][4] != 0))
  390. {
  391. BOOST_CHECK_CLOSE_EX(
  392. boost::math::non_central_chi_squared_distribution<value_type>::find_degrees_of_freedom(boost::math::complement(data[i][1], data[i][2], data[i][4])),
  393. data[i][0], precision, i);
  394. BOOST_CHECK_CLOSE_EX(
  395. boost::math::non_central_chi_squared_distribution<value_type>::find_non_centrality(boost::math::complement(data[i][0], data[i][2], data[i][4])),
  396. data[i][1], precision, i);
  397. }
  398. #ifndef BOOST_NO_EXCEPTIONS
  399. }
  400. catch(const std::exception& e)
  401. {
  402. BOOST_ERROR(e.what());
  403. }
  404. #endif
  405. }
  406. }
  407. #endif
  408. }
  409. template <typename T>
  410. void test_accuracy(T, const char* type_name)
  411. {
  412. #include "nccs.ipp"
  413. do_test_nc_chi_squared<T>(nccs, type_name, "Non Central Chi Squared, medium parameters");
  414. quantile_sanity_check<T>(nccs, type_name, "Non Central Chi Squared, medium parameters");
  415. #include "nccs_big.ipp"
  416. do_test_nc_chi_squared<T>(nccs_big, type_name, "Non Central Chi Squared, large parameters");
  417. quantile_sanity_check<T>(nccs_big, type_name, "Non Central Chi Squared, large parameters");
  418. }