test_qrng_functions.hpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // Copyright Justinas Vygintas Daugmaudis, 2010-2018.
  2. // Use, modification and distribution is subject to the
  3. // Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE-1.0 or http://www.boost.org/LICENSE-1.0)
  5. #ifndef TEST_QRNG_FUNCTIONS_HPP_INCLUDED
  6. #define TEST_QRNG_FUNCTIONS_HPP_INCLUDED
  7. #include <boost/random/uniform_real.hpp>
  8. #include <boost/test/floating_point_comparison.hpp>
  9. #include <sstream>
  10. namespace test {
  11. // Invokes operator() precisely n times. This is to check that
  12. // Engine::discard(n) actually has the same effect.
  13. template<typename Engine>
  14. inline void trivial_discard(Engine& eng, boost::uintmax_t n)
  15. {
  16. for( ; n != 0; --n ) eng();
  17. }
  18. template<typename Engine, typename T, std::size_t Dimension>
  19. inline void match_vector(Engine& eng, T (&pt)[Dimension])
  20. {
  21. BOOST_REQUIRE_EQUAL( eng.dimension(), Dimension ); // paranoid check
  22. boost::uniform_real<T> dist;
  23. for( std::size_t i = 0; i != eng.dimension(); ++i )
  24. {
  25. T val = dist(eng);
  26. // We want to check that quasi-random number generator values differ no
  27. // more than 0.0006% of their value.
  28. BOOST_CHECK_CLOSE(pt[i], val, 0.0006);
  29. }
  30. }
  31. template<typename Engine, typename T, std::size_t Dimension, std::size_t N>
  32. inline void expected_values(T (&pt)[N][Dimension], std::size_t skip)
  33. {
  34. Engine eng(Dimension);
  35. eng.seed(skip);
  36. for( std::size_t i = 0; i != N; ++i )
  37. match_vector(eng, pt[i]);
  38. }
  39. template<typename Engine, typename T>
  40. inline void test_zero_seed(std::size_t dimension)
  41. {
  42. Engine eng(dimension);
  43. Engine other(dimension);
  44. other.seed(0);
  45. // Check that states are equal after zero seed.
  46. boost::uniform_real<T> dist;
  47. BOOST_CHECK( eng == other );
  48. for( std:: size_t i = 0; i != dimension; ++i )
  49. {
  50. T q_val = dist(eng);
  51. T t_val = dist(other);
  52. BOOST_CHECK_CLOSE(q_val, t_val, 0.0001);
  53. }
  54. }
  55. template<typename Engine, typename T, std::size_t Dimension, std::size_t N>
  56. inline void seed_function(T (&pt)[N][Dimension], std::size_t skip)
  57. {
  58. // Test zero seed before doing other tests.
  59. test_zero_seed<Engine, T>(Dimension);
  60. Engine eng(Dimension);
  61. for( std::size_t i = 0; i != N; ++i )
  62. {
  63. // For all N seeds an engine
  64. // and checks if the expected values match.
  65. eng.seed(skip + i);
  66. match_vector(eng, pt[i]);
  67. }
  68. }
  69. template<typename Engine, typename T, std::size_t Dimension, std::size_t N>
  70. inline void discard_function(T (&pt)[N][Dimension], std::size_t skip)
  71. {
  72. Engine eng(Dimension), trivial(Dimension), streamed(Dimension), initial_state(Dimension);
  73. boost::uniform_real<T> dist;
  74. const std::size_t element_count = N * Dimension;
  75. const T* pt_array = reinterpret_cast<T *>(boost::addressof(pt));
  76. std::stringstream ss;
  77. initial_state.seed(skip);
  78. for (std::size_t step = 0; step != element_count; ++step)
  79. {
  80. // Init to the same state
  81. eng = initial_state;
  82. trivial = initial_state;
  83. // Discards have to have the same effect
  84. eng.discard(step);
  85. trivial_discard(trivial, step);
  86. // test serialization to stream
  87. ss.str(std::string()); // clear stream
  88. ss << eng;
  89. ss >> streamed;
  90. // Therefore, states are equal
  91. BOOST_CHECK( eng == trivial );
  92. BOOST_CHECK( eng == streamed );
  93. // Now, let's check whether they really produce the same sequence
  94. T q_val = dist(eng);
  95. T t_val = dist(trivial);
  96. T s_val = dist(streamed);
  97. BOOST_CHECK_CLOSE(q_val, t_val, 0.0001);
  98. BOOST_CHECK_CLOSE(q_val, s_val, 0.0001);
  99. // ~ BOOST_CHECK(q_val == t_val), but those are floating point values,
  100. // so strict equality check may fail unnecessarily
  101. // States remain equal!
  102. BOOST_CHECK( eng == trivial );
  103. BOOST_CHECK( eng == streamed );
  104. // We want to check that quasi-random number generator values differ no
  105. // more than 0.0006% of their value.
  106. BOOST_CHECK_CLOSE(pt_array[step], q_val, 0.0006);
  107. }
  108. }
  109. inline bool accept_all_exceptions(const std::exception& e)
  110. {
  111. BOOST_TEST_MESSAGE( e.what() );
  112. return true;
  113. }
  114. template<typename Engine>
  115. void test_max_seed(std::size_t dim)
  116. {
  117. typedef typename Engine::size_type size_type;
  118. static const size_type maxseed = Engine::max();
  119. Engine eng(dim);
  120. eng.seed(maxseed-1); // must succeed
  121. eng(); // skip one element
  122. BOOST_REQUIRE_EXCEPTION( eng.seed(maxseed), std::range_error, accept_all_exceptions );
  123. Engine other(dim);
  124. other.seed(maxseed-1); // must succeed
  125. other(); // skip one element, too
  126. // States remain the same even after unsuccessful seeding for eng.
  127. BOOST_CHECK( eng == other );
  128. BOOST_CHECK( eng() == other() );
  129. }
  130. template<typename Generator>
  131. void test_max_discard(std::size_t dim)
  132. {
  133. typedef typename Generator::type engine_type;
  134. static const boost::uintmax_t maxdiscard = dim * engine_type::max();
  135. // Max discard limit
  136. {
  137. engine_type eng(dim);
  138. eng.discard(maxdiscard-1); // must succeed
  139. eng(); // the very last element
  140. BOOST_REQUIRE_EXCEPTION( eng(), std::range_error, accept_all_exceptions );
  141. engine_type other(dim);
  142. BOOST_CHECK( eng != other ); // test that comparison does not overflow
  143. other(); // the very first element
  144. other.discard(maxdiscard-1); // must succeed
  145. BOOST_CHECK( eng == other );
  146. BOOST_REQUIRE_EXCEPTION( other(), std::range_error, accept_all_exceptions );
  147. }
  148. // Overdiscarding
  149. {
  150. engine_type eng(dim);
  151. eng.discard(maxdiscard); // must succeed, since it's maxdiscard operator() invocations
  152. // must fail because after discarding the whole sequence
  153. // we can't put the eng to any valid sequence producing state
  154. BOOST_REQUIRE_EXCEPTION( eng(), std::range_error, accept_all_exceptions );
  155. // Plain overdiscarding by 1
  156. engine_type other(dim);
  157. BOOST_REQUIRE_EXCEPTION( other.discard(maxdiscard+1), std::range_error, accept_all_exceptions );
  158. }
  159. // Test wraparound
  160. {
  161. engine_type eng(dim);
  162. // must fail either because seq_count overflow check is triggered,
  163. // or because this discard violates seeding bitcount constraint
  164. BOOST_REQUIRE_EXCEPTION( eng.discard(maxdiscard*2), std::range_error, accept_all_exceptions );
  165. }
  166. }
  167. } // namespace test
  168. #define QRNG_VALIDATION_TEST_FUNCTIONS(QRNG) \
  169. \
  170. typedef boost::random::QRNG engine_t; \
  171. \
  172. template<typename T, std::size_t Dimension, std::size_t N> \
  173. inline void test_##QRNG##_values(T (&pt)[N][Dimension], std::size_t skip) \
  174. { \
  175. test::expected_values<engine_t>(pt, skip); \
  176. } \
  177. \
  178. template<typename T, std::size_t Dimension, std::size_t N> \
  179. inline void test_##QRNG##_seed(T (&pt)[N][Dimension], std::size_t skip) \
  180. { \
  181. test::seed_function<engine_t>(pt, skip); \
  182. } \
  183. \
  184. template<typename T, std::size_t Dimension, std::size_t N> \
  185. inline void test_##QRNG##_discard(T (&pt)[N][Dimension], std::size_t skip) \
  186. { \
  187. test::discard_function<engine_t>(pt, skip); \
  188. } \
  189. inline void test_##QRNG##_max_seed() \
  190. { \
  191. test::test_max_seed<engine_t>(2); \
  192. } \
  193. inline void test_##QRNG##_max_dimension(std::size_t dim) \
  194. { \
  195. engine_t eng(dim); /*must succeed*/ \
  196. BOOST_REQUIRE_EXCEPTION( engine_t(dim+1), std::invalid_argument, test::accept_all_exceptions ); \
  197. } \
  198. \
  199. BOOST_AUTO_TEST_CASE( test_##QRNG##_zero_dimension_fails ) \
  200. { \
  201. BOOST_REQUIRE_EXCEPTION( engine_t(0), std::invalid_argument, test::accept_all_exceptions ); \
  202. } \
  203. /**/
  204. #define QRNG_VALIDATION_TEST_DISCARD(QRNG) \
  205. \
  206. template <typename IntType, unsigned w> \
  207. struct gen_engine \
  208. { \
  209. typedef boost::random::QRNG##_engine<IntType, w> type; \
  210. }; \
  211. \
  212. inline void test_##QRNG##_max_discard() \
  213. { \
  214. static const std::size_t dim = 2;\
  215. \
  216. /* test full 8 bits */ \
  217. test::test_max_discard<gen_engine<boost::uint8_t, 8u> >(dim); \
  218. \
  219. /* test 7 bits */ \
  220. test::test_max_discard<gen_engine<boost::uint8_t, 7u> >(dim); \
  221. \
  222. /* test 6 bits for a good measure */ \
  223. test::test_max_discard<gen_engine<boost::uint8_t, 6u> >(dim); \
  224. } \
  225. /**/
  226. #endif // TEST_QRNG_FUNCTIONS_HPP_INCLUDED