test_hyperexponential.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /* test_hyperexponential.cpp
  2. *
  3. * Copyright Marco Guazzone 2014
  4. * Distributed under the Boost Software License, Version 1.0. (See
  5. * accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. *
  8. * \author Marco Guazzone (marco.guazzone@gmail.com)
  9. *
  10. */
  11. #include <boost/exception/diagnostic_information.hpp>
  12. #include <boost/lexical_cast.hpp>
  13. #include <boost/math/distributions/hyperexponential.hpp>
  14. #include <boost/numeric/conversion/cast.hpp>
  15. #include <boost/random/hyperexponential_distribution.hpp>
  16. #include <boost/random/mersenne_twister.hpp>
  17. #include <boost/random/uniform_int.hpp>
  18. #include <boost/random/uniform_real.hpp>
  19. #include <boost/range/numeric.hpp>
  20. #include <cstring>
  21. #include <iostream>
  22. #include <numeric>
  23. #include <vector>
  24. #include "statistic_tests.hpp"
  25. // This test unit is similar to the one in "test_real_distribution.ipp", which
  26. // has been changed for the hyperexponential distribution.
  27. // We cannot directly use the original test suite since it doesn't work for
  28. // distributions with vector parameters.
  29. // Also, this implementation has been inspired by the test unit for the
  30. // discrete_distribution class.
  31. #ifndef BOOST_RANDOM_P_CUTOFF
  32. # define BOOST_RANDOM_P_CUTOFF 0.99
  33. #endif
  34. #define BOOST_RANDOM_HYPEREXP_NUM_PHASES_MIN 1
  35. #define BOOST_RANDOM_HYPEREXP_NUM_PHASES_MAX 3
  36. #define BOOST_RANDOM_HYPEREXP_PROBABILITY_MIN 0.01
  37. #define BOOST_RANDOM_HYPEREXP_PROBABILITY_MAX 1.0
  38. #define BOOST_RANDOM_HYPEREXP_RATE_MIN 0.0001
  39. #define BOOST_RANDOM_HYPEREXP_RATE_MAX 1000.0
  40. #define BOOST_RANDOM_HYPEREXP_NUM_TRIALS 1000000ll
  41. namespace /*<unnamed>*/ { namespace detail {
  42. template <typename T>
  43. std::vector<T>& normalize(std::vector<T>& v)
  44. {
  45. if (v.size() == 0)
  46. {
  47. return v;
  48. }
  49. const T sum = std::accumulate(v.begin(), v.end(), static_cast<T>(0));
  50. T final_sum = 0;
  51. const typename std::vector<T>::iterator end = --v.end();
  52. for (typename std::vector<T>::iterator it = v.begin();
  53. it != end;
  54. ++it)
  55. {
  56. *it /= sum;
  57. }
  58. *end = 1 - final_sum; // avoids round off errors, ensures the probs really do sum to 1.
  59. return v;
  60. }
  61. template <typename T>
  62. std::vector<T> normalize_copy(std::vector<T> const& v)
  63. {
  64. std::vector<T> vv(v);
  65. return normalize(vv);
  66. }
  67. template <typename T, typename DistT, typename EngineT>
  68. std::vector<T> make_random_vector(std::size_t n, DistT const& dist, EngineT& eng)
  69. {
  70. std::vector<T> res(n);
  71. for (std::size_t i = 0; i < n; ++i)
  72. {
  73. res[i] = dist(eng);
  74. }
  75. return res;
  76. }
  77. }} // Namespace <unnamed>::detail
  78. bool do_test(std::vector<double> const& probabilities,
  79. std::vector<double> const& rates,
  80. long long max,
  81. boost::mt19937& gen)
  82. {
  83. std::cout << "running hyperexponential(probabilities,rates) " << max << " times: " << std::flush;
  84. boost::math::hyperexponential_distribution<> expected(probabilities, rates);
  85. boost::random::hyperexponential_distribution<> dist(probabilities, rates);
  86. kolmogorov_experiment test(boost::numeric_cast<int>(max));
  87. boost::variate_generator<boost::mt19937&, boost::random::hyperexponential_distribution<> > vgen(gen, dist);
  88. const double prob = test.probability(test.run(vgen, expected));
  89. const bool result = prob < BOOST_RANDOM_P_CUTOFF;
  90. const char* err = result? "" : "*";
  91. std::cout << std::setprecision(17) << prob << err << std::endl;
  92. std::cout << std::setprecision(6);
  93. return result;
  94. }
  95. bool do_tests(int repeat, int max_num_phases, double max_rate, long long trials)
  96. {
  97. boost::mt19937 gen;
  98. boost::random::uniform_int_distribution<> num_phases_dist(BOOST_RANDOM_HYPEREXP_NUM_PHASES_MIN, max_num_phases);
  99. int errors = 0;
  100. for (int i = 0; i < repeat; ++i)
  101. {
  102. const int num_phases = num_phases_dist(gen);
  103. boost::random::uniform_real_distribution<> prob_dist(BOOST_RANDOM_HYPEREXP_PROBABILITY_MIN, BOOST_RANDOM_HYPEREXP_PROBABILITY_MAX);
  104. boost::random::uniform_real_distribution<> rate_dist(BOOST_RANDOM_HYPEREXP_RATE_MIN, max_rate);
  105. const std::vector<double> probabilities = detail::normalize_copy(detail::make_random_vector<double>(num_phases, prob_dist, gen));
  106. const std::vector<double> rates = detail::make_random_vector<double>(num_phases, rate_dist, gen);
  107. if (!do_test(probabilities, rates, trials, gen))
  108. {
  109. ++errors;
  110. }
  111. }
  112. if (errors != 0)
  113. {
  114. std::cout << "*** " << errors << " errors detected ***" << std::endl;
  115. }
  116. return errors == 0;
  117. }
  118. int usage()
  119. {
  120. std::cerr << "Usage: test_hyperexponential"
  121. " -r <repeat>"
  122. " -num_phases"
  123. " <max num_phases>"
  124. " -rate"
  125. " <max rate>"
  126. " -t <trials>" << std::endl;
  127. return 2;
  128. }
  129. template<class T>
  130. bool handle_option(int& argc, char**& argv, const char* opt, T& value)
  131. {
  132. if (std::strcmp(argv[0], opt) == 0 && argc > 1)
  133. {
  134. --argc;
  135. ++argv;
  136. value = boost::lexical_cast<T>(argv[0]);
  137. return true;
  138. }
  139. else
  140. {
  141. return false;
  142. }
  143. }
  144. int main(int argc, char** argv)
  145. {
  146. int repeat = 1;
  147. int max_num_phases = BOOST_RANDOM_HYPEREXP_NUM_PHASES_MAX;
  148. double max_rate = BOOST_RANDOM_HYPEREXP_RATE_MAX;
  149. long long trials = BOOST_RANDOM_HYPEREXP_NUM_TRIALS;
  150. if (argc > 0)
  151. {
  152. --argc;
  153. ++argv;
  154. }
  155. while(argc > 0)
  156. {
  157. if (argv[0][0] != '-')
  158. {
  159. return usage();
  160. }
  161. else if (!handle_option(argc, argv, "-r", repeat)
  162. && !handle_option(argc, argv, "-num_phases", max_num_phases)
  163. && !handle_option(argc, argv, "-rate", max_rate)
  164. && !handle_option(argc, argv, "-t", trials))
  165. {
  166. return usage();
  167. }
  168. --argc;
  169. ++argv;
  170. }
  171. try
  172. {
  173. if (do_tests(repeat, max_num_phases, max_rate, trials))
  174. {
  175. return 0;
  176. }
  177. else
  178. {
  179. return EXIT_FAILURE;
  180. }
  181. }
  182. catch(...)
  183. {
  184. std::cerr << boost::current_exception_diagnostic_information() << std::endl;
  185. return EXIT_FAILURE;
  186. }
  187. }