test_pFq.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Copyright John Maddock 2006.
  2. // Copyright Paul A. Bristow 2007, 2009
  3. // Use, modification and distribution are subject to the
  4. // Boost Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error
  7. #include <boost/math/concepts/real_concept.hpp>
  8. #include <boost/math/special_functions/math_fwd.hpp>
  9. #define BOOST_TEST_MAIN
  10. #include <boost/test/unit_test.hpp>
  11. #include <boost/test/tools/floating_point_comparison.hpp>
  12. #include <boost/math/tools/stats.hpp>
  13. #include <boost/math/tools/test.hpp>
  14. #include <boost/math/tools/big_constant.hpp>
  15. #include <boost/math/constants/constants.hpp>
  16. #include <boost/type_traits/is_floating_point.hpp>
  17. #include <boost/array.hpp>
  18. #include "functor.hpp"
  19. #include "handle_test_result.hpp"
  20. #include "table_type.hpp"
  21. #include <boost/math/special_functions/hypergeometric_pFq.hpp>
  22. #include <boost/math/special_functions/relative_difference.hpp>
  23. #ifdef BOOST_MSVC
  24. #pragma warning(disable:4127)
  25. #endif
  26. #ifndef SC_
  27. #define SC_(x) BOOST_MATH_BIG_CONSTANT(T, 1000000, x)
  28. #endif
  29. template <class Seq>
  30. bool is_small_a(const Seq& a)
  31. {
  32. if (a.size() == 1)
  33. {
  34. auto v = *a.begin();
  35. if ((v > -14) && (v < 1))
  36. return true;
  37. }
  38. return false;
  39. }
  40. template <class Seq>
  41. bool has_negative_ab(const Seq& a, const Seq& b)
  42. {
  43. for(auto p = a.begin(); p != a.end(); ++p)
  44. {
  45. if(*p < 0)
  46. return true;
  47. }
  48. for(auto p = b.begin(); p != b.end(); ++p)
  49. {
  50. if(*p < 0)
  51. return true;
  52. }
  53. return false;
  54. }
  55. template <class T>
  56. void check_pFq_result(const T& result, const T& norm, const T& expect, const std::initializer_list<T>& a, const std::initializer_list<T>& b, const T& z)
  57. {
  58. //
  59. // Ideally the error rate we calculate from comparing norm to result
  60. // should be larger than the actual error. However, in practice even
  61. // if all the terms are positive and norm == result there will still
  62. // be a small error from the actual summation (we could work out how
  63. // much from the number of terms summed, but that's overkill for this)
  64. // so we add a small fudge factor when comparing errors:
  65. //
  66. T err = boost::math::relative_difference(result, expect);
  67. T found_err = norm / fabs(result);
  68. T fudge_factor = 25;
  69. if (is_small_a(a))
  70. fudge_factor *= 4; // not sure why??
  71. if ((has_negative_ab(a, b)) || ((a.size() == 2) && (b.size() == 1)) || (boost::math::tools::epsilon<T>() < boost::math::tools::epsilon<double>()))
  72. {
  73. T min_err = boost::math::tools::epsilon<T>() * 600 / found_err;
  74. fudge_factor = (std::max)(fudge_factor, min_err);
  75. }
  76. if ((((err > fudge_factor * found_err) && (found_err < 1)) || (boost::math::isnan)(found_err)) && (!(boost::math::isinf)(result)))
  77. {
  78. std::cout << "Found error = " << err << " error from norm = " << found_err << std::endl;
  79. std::cout << "Testing fudge factor = " << fudge_factor << std::endl;
  80. std::cout << " a = ";
  81. for (auto pa = a.begin(); pa != a.end(); ++pa)
  82. std::cout << *pa << ",";
  83. std::cout << "\n b = ";
  84. for (auto pb = b.begin(); pb != b.end(); ++pb)
  85. std::cout << *pb << ",";
  86. std::cout << "\n z = " << z << std::endl;
  87. //
  88. // This will fail if we've got here:
  89. //
  90. BOOST_CHECK_LE(err, fudge_factor * found_err);
  91. BOOST_CHECK(!(boost::math::isnan)(found_err));
  92. }
  93. }
  94. template <class T>
  95. void test_spots_1F0(T, const char*)
  96. {
  97. using std::pow;
  98. T tolerance = boost::math::tools::epsilon<T>() * 1000;
  99. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(-3) }, {}, T(2)), T(-1), tolerance);
  100. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(-3) }, {}, T(4)), T(-27), tolerance);
  101. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(-3) }, {}, T(0.5)), T(0.125), tolerance);
  102. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(3) }, {}, T(0.5)), T(8), tolerance);
  103. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(3) }, {}, T(2)), T(-1), tolerance);
  104. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(3) }, {}, T(4)), T(T(-1) / 27), tolerance);
  105. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(3) }, {}, T(-0.5)), pow(T(1.5), -3), tolerance);
  106. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(3) }, {}, T(-2)), T(1 / T(27)), tolerance);
  107. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(3) }, {}, T(-4)), T(T(1) / 125), tolerance);
  108. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(-3) }, {}, T(-0.5)), pow(T(1.5), 3), tolerance);
  109. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(-3) }, {}, T(-2)), T(27), tolerance);
  110. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({ T(-3) }, {}, T(-4)), T(125), tolerance);
  111. BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({ T(3) }, {}, T(1)), std::domain_error);
  112. //BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({ T(-3) }, {}, T(1)), std::domain_error);
  113. BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({ T(3.25) }, {}, T(1)), std::domain_error);
  114. //BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({ T(-3.25) }, {}, T(1)), std::domain_error);
  115. BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({ T(3.25) }, {}, T(2)), std::domain_error);
  116. BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({ T(-3.25) }, {}, T(2)), std::domain_error);
  117. }
  118. template <class T>
  119. void test_spots_0F1(T, const char*)
  120. {
  121. T tolerance = boost::math::tools::epsilon<T>() * 50000;
  122. BOOST_CHECK_EQUAL(boost::math::hypergeometric_pFq({}, { T(3) }, T(0)), 1);
  123. BOOST_CHECK_EQUAL(boost::math::hypergeometric_pFq({}, { T(-3) }, T(0)), 1);
  124. //BOOST_CHECK_EQUAL(boost::math::hypergeometric_pFq({}, { T(0) }, T(0)), 1);
  125. BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({}, { T(0) }, T(-1)), std::domain_error);
  126. BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({}, { T(-1) }, T(-1)), std::domain_error);
  127. BOOST_CHECK_THROW(boost::math::hypergeometric_pFq({}, { T(-10) }, T(-5)), std::domain_error);
  128. static const boost::array<boost::array<T, 3>, 35> hypergeometric_pFq_integer_data = { {
  129. { SC_(4.0), SC_(-20.0), SC_(-0.012889714201783047561923257996127233830940165138385) },
  130. { SC_(8.0), SC_(-20.0), SC_(0.046498609282365144223175012935939437508273248399881) },
  131. { SC_(12.0), SC_(-20.0), SC_(0.16608847431869756642136191351311569335145459224622) },
  132. { SC_(16.0), SC_(-20.0), SC_(0.27230484709157170329168048388841880599105216477631) },
  133. //{ SC_(20.0), SC_(-20.0), SC_(0.35865872656868844615709101792040025253126126604266) },
  134. { SC_(4.0), SC_(-16.0), SC_(-0.027293644412433023379286103818840667403690937153604) },
  135. { SC_(8.0), SC_(-16.0), SC_(0.098618710511372349330666801041676087431136532039702) },
  136. { SC_(12.0), SC_(-16.0), SC_(0.24360114226383905073379763460037817885919457531523) },
  137. //{ SC_(16.0), SC_(-16.0), SC_(0.35635186318802906043824855864337727878754460163525) },
  138. //{ SC_(20.0), SC_(-16.0), SC_(0.44218381382689101428948260613085371477815110358789) },
  139. { SC_(4.0), SC_(-12.0), SC_(-0.021743572290699436419371120781513860006290363262907) },
  140. { SC_(8.0), SC_(-12.0), SC_(0.19025625754362006866949730683824627505504067855043) },
  141. //{ SC_(12.0), SC_(-12.0), SC_(0.35251228238278927379621049815222218665165551016489) },
  142. //{ SC_(16.0), SC_(-12.0), SC_(0.46415411486674623230458980010115972932474705884865) },
  143. //{ SC_(20.0), SC_(-12.0), SC_(0.54394918325286018927327004362535051310016558628741) },
  144. { SC_(4.0), SC_(-8.0), SC_(0.056818744289274872033266550620647787396712125304880) },
  145. //{ SC_(8.0), SC_(-8.0), SC_(0.34487371876996263249797701802458885718691612997456) },
  146. //{ SC_(12.0), SC_(-8.0), SC_(0.50411654015891701804499796523449656998841355305043) },
  147. //{ SC_(16.0), SC_(-8.0), SC_(0.60191459981670594041254437708158847428118361245442) },
  148. //{ SC_(20.0), SC_(-8.0), SC_(0.66770752550930138035694866478078941681114294465418) },
  149. //{ SC_(4.0), SC_(-4.0), SC_(0.32262860540671645526863760914000166725449779629143) },
  150. //{ SC_(8.0), SC_(-4.0), SC_(0.59755773349355150397404772151441126513126998265958) },
  151. //{ SC_(12.0), SC_(-4.0), SC_(0.71337465206009117934071859694314971137807212605147) },
  152. //{ SC_(16.0), SC_(-4.0), SC_(0.77734333649378860739496954157535257278092349684783) },
  153. //{ SC_(20.0), SC_(-4.0), SC_(0.81794177985447769150469288350369205683856312760890) },
  154. { SC_(4.0), SC_(4.0), SC_(2.5029568338152582758923890008139391395035041790831) },
  155. { SC_(8.0), SC_(4.0), SC_(1.6273673128576761227855719910743734060605725722129) },
  156. { SC_(12.0), SC_(4.0), SC_(1.3898419290864057799739567227851793491657442624207) },
  157. { SC_(16.0), SC_(4.0), SC_(1.2817098157957427946677711269410726972209834860612) },
  158. { SC_(20.0), SC_(4.0), SC_(1.2202539302152377230940386181201477276788392792437) },
  159. { SC_(4.0), SC_(8.0), SC_(5.5616961007411965409200003309686924059253894118586) },
  160. { SC_(8.0), SC_(8.0), SC_(2.5877053985451664722152913482683136948296873738479) },
  161. { SC_(12.0), SC_(8.0), SC_(1.9166410733572697158003086323981583993970490592046) },
  162. { SC_(16.0), SC_(8.0), SC_(1.6370675016890669952237854163997946987362497613701) },
  163. { SC_(20.0), SC_(8.0), SC_(1.4862852701827990444915220582410007454379891584086) },
  164. { SC_(4.0), SC_(12.0), SC_(11.419268276211177842169936131590385979116019595164) },
  165. { SC_(8.0), SC_(12.0), SC_(4.0347215359576567066789638314925802225312840819037) },
  166. { SC_(12.0), SC_(12.0), SC_(2.6242497527837800417573064942486918368886996538285) },
  167. { SC_(16.0), SC_(12.0), SC_(2.0840468784170876805932772732753387258909164486511) },
  168. { SC_(20.0), SC_(12.0), SC_(1.8071042457762091748544382847762106786633952487005) },
  169. { SC_(4.0), SC_(16.0), SC_(22.132051970576036053853444648907108439504682530918) },
  170. { SC_(8.0), SC_(16.0), SC_(6.1850485247748975008808779795786699492711191898792) },
  171. { SC_(12.0), SC_(16.0), SC_(3.5694322843488018916484224923627864928705138154372) },
  172. { SC_(16.0), SC_(16.0), SC_(2.6447371137201451261118187672029372265909501355722) },
  173. { SC_(20.0), SC_(16.0), SC_(2.1934058398888071720297525592515838555602675797235) },
  174. { SC_(4.0), SC_(20.0), SC_(41.021743268279206331672552645354782698296383424328) },
  175. { SC_(8.0), SC_(20.0), SC_(9.3414225299809886395081381945971250426599939097753) },
  176. { SC_(12.0), SC_(20.0), SC_(4.8253866205826406499959001774187695527272168375992) },
  177. { SC_(16.0), SC_(20.0), SC_(3.3462305133519485784864062004430532216764447939942) },
  178. { SC_(20.0), SC_(20.0), SC_(2.6578698872220394617444624241257799193518140676691) },
  179. } };
  180. for (auto row = hypergeometric_pFq_integer_data.begin(); row != hypergeometric_pFq_integer_data.end(); ++row)
  181. {
  182. BOOST_CHECK_CLOSE(boost::math::hypergeometric_pFq({}, { (*row)[0] }, (*row)[1]), (*row)[2], tolerance);
  183. }
  184. }
  185. template <class T>
  186. void test_spots_1F1(T, const char*)
  187. {
  188. #include "hypergeometric_1F1.ipp"
  189. for (auto row = hypergeometric_1F1.begin(); row != hypergeometric_1F1.end(); ++row)
  190. {
  191. try {
  192. T norm;
  193. T result = boost::math::hypergeometric_pFq({ (*row)[0] }, { (*row)[1] }, (*row)[2], &norm);
  194. check_pFq_result(result, norm, (*row)[3], { (*row)[0] }, { (*row)[1] }, (*row)[2]);
  195. }
  196. catch (const boost::math::evaluation_error&) {}
  197. }
  198. }
  199. template <class T>
  200. void test_spots_1F1_b(T, const char*)
  201. {
  202. #include "hypergeometric_1F1_big.ipp"
  203. for (auto row = hypergeometric_1F1_big.begin(); row != hypergeometric_1F1_big.end(); ++row)
  204. {
  205. try {
  206. T norm;
  207. T result = boost::math::hypergeometric_pFq({ (*row)[0] }, { (*row)[1] }, (*row)[2], &norm);
  208. check_pFq_result(result, norm, (*row)[3], { (*row)[0] }, { (*row)[1] }, (*row)[2]);
  209. }
  210. catch (const boost::math::evaluation_error&) {}
  211. }
  212. }
  213. template <class T>
  214. void test_spots_2F1(T, const char*)
  215. {
  216. #include "hypergeometric_2F1.ipp"
  217. for (auto row = hypergeometric_2F1.begin(); row != hypergeometric_2F1.end(); ++row)
  218. {
  219. try {
  220. T norm;
  221. T result = boost::math::hypergeometric_pFq({ (*row)[0], (*row)[1] }, { (*row)[2] }, (*row)[3], &norm);
  222. check_pFq_result(result, norm, (*row)[4], { (*row)[0], (*row)[1] }, { (*row)[2] }, (*row)[3]);
  223. }
  224. catch (const boost::math::evaluation_error&) {}
  225. }
  226. }
  227. template <class T>
  228. void test_spots_0F2(T, const char*)
  229. {
  230. #include "hypergeometric_0F2.ipp"
  231. for (auto row = hypergeometric_0F2.begin(); row != hypergeometric_0F2.end(); ++row)
  232. {
  233. try {
  234. T norm;
  235. T result = boost::math::hypergeometric_pFq({}, { (*row)[0], (*row)[1] }, (*row)[2], &norm);
  236. check_pFq_result(result, norm, (*row)[3], {}, { (*row)[0], (*row)[1] }, (*row)[2]);
  237. }
  238. catch (const boost::math::evaluation_error&) {}
  239. }
  240. }
  241. template <class T>
  242. void test_spots_1F2(T, const char*)
  243. {
  244. #include "hypergeometric_1F2.ipp"
  245. for (auto row = hypergeometric_1F2.begin(); row != hypergeometric_1F2.end(); ++row)
  246. {
  247. try {
  248. T norm;
  249. T result = boost::math::hypergeometric_pFq({ (*row)[0] }, { (*row)[1], (*row)[2] }, (*row)[3], &norm);
  250. check_pFq_result(result, norm, (*row)[4], { (*row)[0] }, { (*row)[1], (*row)[2] }, (*row)[3]);
  251. }
  252. catch (const boost::math::evaluation_error&) {}
  253. }
  254. }
  255. template <class T>
  256. void test_spots_2F2(T, const char*)
  257. {
  258. #include "hypergeometric_2F2.ipp"
  259. for (auto row = hypergeometric_2F2.begin(); row != hypergeometric_2F2.end(); ++row)
  260. {
  261. try {
  262. T norm;
  263. T result = boost::math::hypergeometric_pFq({ (*row)[0], (*row)[1] }, { (*row)[2], (*row)[3] }, (*row)[4], &norm);
  264. check_pFq_result(result, norm, (*row)[5], { (*row)[0], (*row)[1] }, { (*row)[2], (*row)[3] }, (*row)[4]);
  265. }
  266. catch (const boost::math::evaluation_error&) {}
  267. }
  268. }
  269. template <class T>
  270. void test_spots(T z, const char* type_name)
  271. {
  272. test_spots_1F0(z, type_name);
  273. test_spots_0F1(z, type_name);
  274. test_spots_1F1(z, type_name);
  275. test_spots_1F1_b(z, type_name);
  276. test_spots_0F2(z, type_name);
  277. test_spots_1F2(z, type_name);
  278. test_spots_2F2(z, type_name);
  279. test_spots_2F1(z, type_name);
  280. }