A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
rng-test-suite.cc
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: GPL-2.0-only
3 */
4
5#include "ns3/double.h"
6#include "ns3/random-variable-stream.h"
7#include "ns3/rng-seed-manager.h"
8#include "ns3/test.h"
9
10#include <cmath>
11#include <ctime>
12#include <fstream>
13#include <gsl/gsl_cdf.h>
14#include <gsl/gsl_histogram.h>
15
16using namespace ns3;
17
18/**
19 * \file
20 * \ingroup rng-tests
21 * Random number generators tests.
22 */
23
24/**
25 * \ingroup core-tests
26 * \defgroup rng-tests Random number generators tests
27 */
28
29/**
30 * \ingroup rng-tests
31 *
32 * Fill an array with increasing values, in the [start, end] range.
33 * \param array The array to fill.
34 * \param n The size of the array.
35 * \param start The start value.
36 * \param end The end value.
37 */
38void
39FillHistoRangeUniformly(double* array, uint32_t n, double start, double end)
40{
41 double increment = (end - start) / (n - 1.);
42 double d = start;
43
44 for (uint32_t i = 0; i < n; ++i)
45 {
46 array[i] = d;
47 d += increment;
48 }
49}
50
51/**
52 * \ingroup core-tests
53 *
54 * Test case for uniform distribution random number generator.
55 */
57{
58 public:
59 /// Number of runs.
60 static const uint32_t N_RUNS = 5;
61 /// Number of bins.
62 static const uint32_t N_BINS = 50;
63 /// Number of measurements.
64 static const uint32_t N_MEASUREMENTS = 1000000;
65
67 ~RngUniformTestCase() override;
68
69 /**
70 * Run a chi-squared test on the results of the random number generator.
71 * \param u The random number generator.
72 * \return the chi-squared test result.
73 */
75
76 private:
77 void DoRun() override;
78};
79
81 : TestCase("Uniform Random Number Generator")
82{
83}
84
88
89double
91{
92 gsl_histogram* h = gsl_histogram_alloc(N_BINS);
93 gsl_histogram_set_ranges_uniform(h, 0., 1.);
94
95 for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
96 {
97 gsl_histogram_increment(h, u->GetValue());
98 }
99
100 double tmp[N_BINS];
101
102 double expected = ((double)N_MEASUREMENTS / (double)N_BINS);
103
104 for (uint32_t i = 0; i < N_BINS; ++i)
105 {
106 tmp[i] = gsl_histogram_get(h, i);
107 tmp[i] -= expected;
108 tmp[i] *= tmp[i];
109 tmp[i] /= expected;
110 }
111
112 gsl_histogram_free(h);
113
114 double chiSquared = 0;
115
116 for (uint32_t i = 0; i < N_BINS; ++i)
117 {
118 chiSquared += tmp[i];
119 }
120
121 return chiSquared;
122}
123
124void
126{
127 RngSeedManager::SetSeed(static_cast<uint32_t>(time(nullptr)));
128
129 double sum = 0.;
130 double maxStatistic = gsl_cdf_chisq_Qinv(0.05, N_BINS);
131
132 for (uint32_t i = 0; i < N_RUNS; ++i)
133 {
135 double result = ChiSquaredTest(u);
136 sum += result;
137 }
138
139 sum /= (double)N_RUNS;
140
141 NS_TEST_ASSERT_MSG_LT(sum, maxStatistic, "Chi-squared statistic out of range");
142}
143
144/**
145 * \ingroup rng-tests
146 *
147 * Test case for normal distribution random number generator.
148 */
150{
151 public:
152 /// Number of runs.
153 static const uint32_t N_RUNS = 5;
154 /// Number of bins.
155 static const uint32_t N_BINS = 50;
156 /// Number of measurements.
157 static const uint32_t N_MEASUREMENTS = 1000000;
158
160 ~RngNormalTestCase() override;
161
162 /**
163 * Run a chi-squared test on the results of the random number generator.
164 * \param n The random number generator.
165 * \return the chi-squared test result.
166 */
168
169 private:
170 void DoRun() override;
171};
172
174 : TestCase("Normal Random Number Generator")
175{
176}
177
181
182double
184{
185 gsl_histogram* h = gsl_histogram_alloc(N_BINS);
186
187 double range[N_BINS + 1];
188 FillHistoRangeUniformly(range, N_BINS + 1, -4., 4.);
189 range[0] = -std::numeric_limits<double>::max();
190 range[N_BINS] = std::numeric_limits<double>::max();
191
192 gsl_histogram_set_ranges(h, range, N_BINS + 1);
193
194 double expected[N_BINS];
195
196 double sigma = 1.;
197
198 for (uint32_t i = 0; i < N_BINS; ++i)
199 {
200 expected[i] = gsl_cdf_gaussian_P(range[i + 1], sigma) - gsl_cdf_gaussian_P(range[i], sigma);
201 expected[i] *= N_MEASUREMENTS;
202 }
203
204 for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
205 {
206 gsl_histogram_increment(h, n->GetValue());
207 }
208
209 double tmp[N_BINS];
210
211 for (uint32_t i = 0; i < N_BINS; ++i)
212 {
213 tmp[i] = gsl_histogram_get(h, i);
214 tmp[i] -= expected[i];
215 tmp[i] *= tmp[i];
216 tmp[i] /= expected[i];
217 }
218
219 gsl_histogram_free(h);
220
221 double chiSquared = 0;
222
223 for (uint32_t i = 0; i < N_BINS; ++i)
224 {
225 chiSquared += tmp[i];
226 }
227
228 return chiSquared;
229}
230
231void
233{
234 RngSeedManager::SetSeed(static_cast<uint32_t>(time(nullptr)));
235
236 double sum = 0.;
237 double maxStatistic = gsl_cdf_chisq_Qinv(0.05, N_BINS);
238
239 for (uint32_t i = 0; i < N_RUNS; ++i)
240 {
242 double result = ChiSquaredTest(n);
243 sum += result;
244 }
245
246 sum /= (double)N_RUNS;
247
248 NS_TEST_ASSERT_MSG_LT(sum, maxStatistic, "Chi-squared statistic out of range");
249}
250
251/**
252 * \ingroup rng-tests
253 *
254 * Test case for exponential distribution random number generator.
255 */
257{
258 public:
259 /// Number of runs.
260 static const uint32_t N_RUNS = 5;
261 /// Number of bins.
262 static const uint32_t N_BINS = 50;
263 /// Number of measurements.
264 static const uint32_t N_MEASUREMENTS = 1000000;
265
267 ~RngExponentialTestCase() override;
268
269 /**
270 * Run a chi-squared test on the results of the random number generator.
271 * \param n The random number generator.
272 * \return the chi-squared test result.
273 */
275
276 private:
277 void DoRun() override;
278};
279
281 : TestCase("Exponential Random Number Generator")
282{
283}
284
288
289double
291{
292 gsl_histogram* h = gsl_histogram_alloc(N_BINS);
293
294 double range[N_BINS + 1];
295 FillHistoRangeUniformly(range, N_BINS + 1, 0., 10.);
296 range[N_BINS] = std::numeric_limits<double>::max();
297
298 gsl_histogram_set_ranges(h, range, N_BINS + 1);
299
300 double expected[N_BINS];
301
302 double mu = 1.;
303
304 for (uint32_t i = 0; i < N_BINS; ++i)
305 {
306 expected[i] = gsl_cdf_exponential_P(range[i + 1], mu) - gsl_cdf_exponential_P(range[i], mu);
307 expected[i] *= N_MEASUREMENTS;
308 }
309
310 for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
311 {
312 gsl_histogram_increment(h, e->GetValue());
313 }
314
315 double tmp[N_BINS];
316
317 for (uint32_t i = 0; i < N_BINS; ++i)
318 {
319 tmp[i] = gsl_histogram_get(h, i);
320 tmp[i] -= expected[i];
321 tmp[i] *= tmp[i];
322 tmp[i] /= expected[i];
323 }
324
325 gsl_histogram_free(h);
326
327 double chiSquared = 0;
328
329 for (uint32_t i = 0; i < N_BINS; ++i)
330 {
331 chiSquared += tmp[i];
332 }
333
334 return chiSquared;
335}
336
337void
339{
340 RngSeedManager::SetSeed(static_cast<uint32_t>(time(nullptr)));
341
342 double sum = 0.;
343 double maxStatistic = gsl_cdf_chisq_Qinv(0.05, N_BINS);
344
345 for (uint32_t i = 0; i < N_RUNS; ++i)
346 {
348 double result = ChiSquaredTest(e);
349 sum += result;
350 }
351
352 sum /= (double)N_RUNS;
353
354 NS_TEST_ASSERT_MSG_LT(sum, maxStatistic, "Chi-squared statistic out of range");
355}
356
357/**
358 * \ingroup rng-tests
359 *
360 * Test case for pareto distribution random number generator.
361 */
363{
364 public:
365 /// Number of runs.
366 static const uint32_t N_RUNS = 5;
367 /// Number of bins.
368 static const uint32_t N_BINS = 50;
369 /// Number of measurements.
370 static const uint32_t N_MEASUREMENTS = 1000000;
371
373 ~RngParetoTestCase() override;
374
375 /**
376 * Run a chi-squared test on the results of the random number generator.
377 * \param p The random number generator.
378 * \return the chi-squared test result.
379 */
381
382 private:
383 void DoRun() override;
384};
385
387 : TestCase("Pareto Random Number Generator")
388{
389}
390
394
395double
397{
398 gsl_histogram* h = gsl_histogram_alloc(N_BINS);
399
400 double range[N_BINS + 1];
401 FillHistoRangeUniformly(range, N_BINS + 1, 1., 10.);
402 range[N_BINS] = std::numeric_limits<double>::max();
403
404 gsl_histogram_set_ranges(h, range, N_BINS + 1);
405
406 double expected[N_BINS];
407
408 double a = 1.5;
409 double b = 0.33333333;
410
411 // mean is 1 with these values
412
413 for (uint32_t i = 0; i < N_BINS; ++i)
414 {
415 expected[i] = gsl_cdf_pareto_P(range[i + 1], a, b) - gsl_cdf_pareto_P(range[i], a, b);
416 expected[i] *= N_MEASUREMENTS;
417 }
418
419 for (uint32_t i = 0; i < N_MEASUREMENTS; ++i)
420 {
421 gsl_histogram_increment(h, p->GetValue());
422 }
423
424 double tmp[N_BINS];
425
426 for (uint32_t i = 0; i < N_BINS; ++i)
427 {
428 tmp[i] = gsl_histogram_get(h, i);
429 tmp[i] -= expected[i];
430 tmp[i] *= tmp[i];
431 tmp[i] /= expected[i];
432 }
433
434 gsl_histogram_free(h);
435
436 double chiSquared = 0;
437
438 for (uint32_t i = 0; i < N_BINS; ++i)
439 {
440 chiSquared += tmp[i];
441 }
442
443 return chiSquared;
444}
445
446void
448{
449 RngSeedManager::SetSeed(static_cast<uint32_t>(time(nullptr)));
450
451 double sum = 0.;
452 double maxStatistic = gsl_cdf_chisq_Qinv(0.05, N_BINS);
453
454 for (uint32_t i = 0; i < N_RUNS; ++i)
455 {
457 e->SetAttribute("Shape", DoubleValue(1.5));
458 e->SetAttribute("Scale", DoubleValue(0.33333333));
459 double result = ChiSquaredTest(e);
460 sum += result;
461 }
462
463 sum /= (double)N_RUNS;
464
465 NS_TEST_ASSERT_MSG_LT(sum, maxStatistic, "Chi-squared statistic out of range");
466}
467
468/**
469 * \ingroup rng-tests
470 *
471 * \brief The random number generators Test Suite.
472 */
474{
475 public:
476 RngTestSuite();
477};
478
480 : TestSuite("random-number-generators", Type::UNIT)
481{
482 AddTestCase(new RngUniformTestCase, TestCase::Duration::QUICK);
483 AddTestCase(new RngNormalTestCase, TestCase::Duration::QUICK);
484 AddTestCase(new RngExponentialTestCase, TestCase::Duration::QUICK);
485 AddTestCase(new RngParetoTestCase, TestCase::Duration::QUICK);
486}
487
488static RngTestSuite g_rngTestSuite; //!< Static variable for test initialization
Test case for exponential distribution random number generator.
static const uint32_t N_BINS
Number of bins.
void DoRun() override
Implementation to actually run this TestCase.
static const uint32_t N_MEASUREMENTS
Number of measurements.
double ChiSquaredTest(Ptr< ExponentialRandomVariable > n)
Run a chi-squared test on the results of the random number generator.
static const uint32_t N_RUNS
Number of runs.
Test case for normal distribution random number generator.
~RngNormalTestCase() override
double ChiSquaredTest(Ptr< NormalRandomVariable > n)
Run a chi-squared test on the results of the random number generator.
static const uint32_t N_MEASUREMENTS
Number of measurements.
static const uint32_t N_RUNS
Number of runs.
void DoRun() override
Implementation to actually run this TestCase.
static const uint32_t N_BINS
Number of bins.
Test case for pareto distribution random number generator.
static const uint32_t N_RUNS
Number of runs.
~RngParetoTestCase() override
double ChiSquaredTest(Ptr< ParetoRandomVariable > p)
Run a chi-squared test on the results of the random number generator.
void DoRun() override
Implementation to actually run this TestCase.
static const uint32_t N_BINS
Number of bins.
static const uint32_t N_MEASUREMENTS
Number of measurements.
The random number generators Test Suite.
Test case for uniform distribution random number generator.
double ChiSquaredTest(Ptr< UniformRandomVariable > u)
Run a chi-squared test on the results of the random number generator.
static const uint32_t N_MEASUREMENTS
Number of measurements.
static const uint32_t N_BINS
Number of bins.
~RngUniformTestCase() override
static const uint32_t N_RUNS
Number of runs.
void DoRun() override
Implementation to actually run this TestCase.
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition double.h:31
Smart pointer class similar to boost::intrusive_ptr.
static void SetSeed(uint32_t seed)
Set the seed.
encapsulates test code
Definition test.h:1050
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:292
A suite of tests to run.
Definition test.h:1267
Type
Type of test.
Definition test.h:1274
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
void FillHistoRangeUniformly(double *array, uint32_t n, double start, double end)
Fill an array with increasing values, in the [start, end] range.
#define NS_TEST_ASSERT_MSG_LT(actual, limit, msg)
Test that an actual value is less than a limit and report and abort if not.
Definition test.h:699
Every class exported by the ns3 library is enclosed in the ns3 namespace.
static RngTestSuite g_rngTestSuite
Static variable for test initialization.