gray_coded_qrng.hpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /* boost random/detail/gray_coded_qrng.hpp header file
  2. *
  3. * Copyright Justinas Vygintas Daugmaudis 2010-2018
  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. #ifndef BOOST_RANDOM_DETAIL_GRAY_CODED_QRNG_HPP
  9. #define BOOST_RANDOM_DETAIL_GRAY_CODED_QRNG_HPP
  10. #include <boost/random/detail/qrng_base.hpp>
  11. #include <boost/core/bit.hpp> // lsb
  12. #include <boost/throw_exception.hpp>
  13. #include <stdexcept>
  14. #include <functional> // bit_xor
  15. #include <algorithm>
  16. #include <boost/type_traits/conditional.hpp>
  17. #include <boost/integer/integer_mask.hpp>
  18. //!\file
  19. //!Describes the gray-coded quasi-random number generator base class template.
  20. namespace boost {
  21. namespace random {
  22. namespace qrng_detail {
  23. template<class T> static int lsb( T x )
  24. {
  25. if( x == 0 )
  26. {
  27. BOOST_THROW_EXCEPTION( std::range_error( "qrng_detail::lsb: argument is 0" ) );
  28. }
  29. return boost::core::countr_zero( x );
  30. }
  31. template<class T> static int msb( T x )
  32. {
  33. if( x == 0 )
  34. {
  35. BOOST_THROW_EXCEPTION( std::range_error( "qrng_detail::msb: argument is 0" ) );
  36. }
  37. return std::numeric_limits<T>::digits - 1 - boost::core::countl_zero( x );
  38. }
  39. template<typename LatticeT>
  40. class gray_coded_qrng
  41. : public qrng_base<
  42. gray_coded_qrng<LatticeT>
  43. , LatticeT
  44. , typename LatticeT::value_type
  45. >
  46. {
  47. public:
  48. typedef typename LatticeT::value_type result_type;
  49. typedef result_type size_type;
  50. private:
  51. typedef gray_coded_qrng<LatticeT> self_t;
  52. typedef qrng_base<self_t, LatticeT, size_type> base_t;
  53. // The base needs to access modifying member f-ns, and we
  54. // don't want these functions to be available for the public use
  55. friend class qrng_base<self_t, LatticeT, size_type>;
  56. // Respect lattice bit_count here
  57. struct check_nothing {
  58. inline static void bit_pos(unsigned) {}
  59. inline static void code_size(size_type) {}
  60. };
  61. struct check_bit_range {
  62. static void raise_bit_count() {
  63. boost::throw_exception( std::range_error("gray_coded_qrng: bit_count") );
  64. }
  65. inline static void bit_pos(unsigned bit_pos) {
  66. if (bit_pos >= LatticeT::bit_count)
  67. raise_bit_count();
  68. }
  69. inline static void code_size(size_type code) {
  70. if (code > (self_t::max)())
  71. raise_bit_count();
  72. }
  73. };
  74. // We only want to check whether bit pos is outside the range if given bit_count
  75. // is narrower than the size_type, otherwise checks compile to nothing.
  76. BOOST_STATIC_ASSERT(LatticeT::bit_count <= std::numeric_limits<size_type>::digits);
  77. typedef typename conditional<
  78. ((LatticeT::bit_count) < std::numeric_limits<size_type>::digits)
  79. , check_bit_range
  80. , check_nothing
  81. >::type check_bit_range_t;
  82. public:
  83. //!Returns: Tight lower bound on the set of values returned by operator().
  84. //!
  85. //!Throws: nothing.
  86. static BOOST_CONSTEXPR result_type min BOOST_PREVENT_MACRO_SUBSTITUTION ()
  87. { return 0; }
  88. //!Returns: Tight upper bound on the set of values returned by operator().
  89. //!
  90. //!Throws: nothing.
  91. static BOOST_CONSTEXPR result_type max BOOST_PREVENT_MACRO_SUBSTITUTION ()
  92. { return low_bits_mask_t<LatticeT::bit_count>::sig_bits; }
  93. explicit gray_coded_qrng(std::size_t dimension)
  94. : base_t(dimension)
  95. {}
  96. // default copy c-tor is fine
  97. // default assignment operator is fine
  98. void seed()
  99. {
  100. set_zero_state();
  101. update_quasi(0);
  102. base_t::reset_seq(0);
  103. }
  104. void seed(const size_type init)
  105. {
  106. if (init != this->curr_seq())
  107. {
  108. // We don't want negative seeds.
  109. check_seed_sign(init);
  110. size_type seq_code = init + 1;
  111. if (BOOST_UNLIKELY(!(init < seq_code)))
  112. boost::throw_exception( std::range_error("gray_coded_qrng: seed") );
  113. seq_code ^= (seq_code >> 1);
  114. // Fail if we see that seq_code is outside bit range.
  115. // We do that before we even touch engine state.
  116. check_bit_range_t::code_size(seq_code);
  117. set_zero_state();
  118. for (unsigned r = 0; seq_code != 0; ++r, seq_code >>= 1)
  119. {
  120. if (seq_code & static_cast<size_type>(1))
  121. update_quasi(r);
  122. }
  123. }
  124. // Everything went well, set the new seq count
  125. base_t::reset_seq(init);
  126. }
  127. private:
  128. void compute_seq(size_type seq)
  129. {
  130. // Find the position of the least-significant zero in sequence count.
  131. // This is the bit that changes in the Gray-code representation as
  132. // the count is advanced.
  133. // Xor'ing with max() has the effect of flipping all the bits in seq,
  134. // except for the sign bit.
  135. unsigned r = qrng_detail::lsb(static_cast<size_type>(seq ^ (self_t::max)()));
  136. check_bit_range_t::bit_pos(r);
  137. update_quasi(r);
  138. }
  139. void update_quasi(unsigned r)
  140. {
  141. // Calculate the next state.
  142. std::transform(this->state_begin(), this->state_end(),
  143. this->lattice.iter_at(r * this->dimension()), this->state_begin(),
  144. std::bit_xor<result_type>());
  145. }
  146. void set_zero_state()
  147. {
  148. std::fill(this->state_begin(), this->state_end(), result_type /*zero*/ ());
  149. }
  150. };
  151. } // namespace qrng_detail
  152. } // namespace random
  153. } // namespace boost
  154. #endif // BOOST_RANDOM_DETAIL_GRAY_CODED_QRNG_HPP