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