1 /* boost random/sobol.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 9 #ifndef BOOST_RANDOM_SOBOL_HPP 10 #define BOOST_RANDOM_SOBOL_HPP 11 12 #include <boost/random/detail/sobol_table.hpp> 13 #include <boost/random/detail/gray_coded_qrng.hpp> 14 15 namespace boost { 16 namespace random { 17 18 /** @cond */ 19 namespace qrng_detail { 20 21 // sobol_lattice sets up the random-number generator to produce a Sobol 22 // sequence of at most max dims-dimensional quasi-random vectors. 23 // Adapted from ACM TOMS algorithm 659, see 24 25 // http://doi.acm.org/10.1145/42288.214372 26 27 template<typename UIntType, unsigned w, typename SobolTables> 28 struct sobol_lattice 29 { 30 typedef UIntType value_type; 31 32 BOOST_STATIC_ASSERT(w > 0u); 33 BOOST_STATIC_CONSTANT(unsigned, bit_count = w); 34 35 private: 36 typedef std::vector<value_type> container_type; 37 38 public: sobol_latticeboost::random::qrng_detail::sobol_lattice39 explicit sobol_lattice(std::size_t dimension) 40 { 41 resize(dimension); 42 } 43 44 // default copy c-tor is fine 45 resizeboost::random::qrng_detail::sobol_lattice46 void resize(std::size_t dimension) 47 { 48 dimension_assert("Sobol", dimension, SobolTables::max_dimension); 49 50 // Initialize the bit array 51 container_type cj(bit_count * dimension); 52 53 // Initialize direction table in dimension 0 54 for (unsigned k = 0; k != bit_count; ++k) 55 cj[dimension*k] = static_cast<value_type>(1); 56 57 // Initialize in remaining dimensions. 58 for (std::size_t dim = 1; dim < dimension; ++dim) 59 { 60 const typename SobolTables::value_type poly = SobolTables::polynomial(dim-1); 61 if (poly > std::numeric_limits<value_type>::max()) { 62 boost::throw_exception( std::range_error("sobol: polynomial value outside the given value type range") ); 63 } 64 const unsigned degree = multiprecision::msb(poly); // integer log2(poly) 65 66 // set initial values of m from table 67 for (unsigned k = 0; k != degree; ++k) 68 cj[dimension*k + dim] = SobolTables::minit(dim-1, k); 69 70 // Calculate remaining elements for this dimension, 71 // as explained in Bratley+Fox, section 2. 72 for (unsigned j = degree; j < bit_count; ++j) 73 { 74 typename SobolTables::value_type p_i = poly; 75 const std::size_t bit_offset = dimension*j + dim; 76 77 cj[bit_offset] = cj[dimension*(j-degree) + dim]; 78 for (unsigned k = 0; k != degree; ++k, p_i >>= 1) 79 { 80 int rem = degree - k; 81 cj[bit_offset] ^= ((p_i & 1) * cj[dimension*(j-rem) + dim]) << rem; 82 } 83 } 84 } 85 86 // Shift columns by appropriate power of 2. 87 unsigned p = 1u; 88 for (int j = bit_count-1-1; j >= 0; --j, ++p) 89 { 90 const std::size_t bit_offset = dimension * j; 91 for (std::size_t dim = 0; dim != dimension; ++dim) 92 cj[bit_offset + dim] <<= p; 93 } 94 95 bits.swap(cj); 96 } 97 iter_atboost::random::qrng_detail::sobol_lattice98 typename container_type::const_iterator iter_at(std::size_t n) const 99 { 100 BOOST_ASSERT(!(n > bits.size())); 101 return bits.begin() + n; 102 } 103 104 private: 105 container_type bits; 106 }; 107 108 } // namespace qrng_detail 109 110 typedef detail::qrng_tables::sobol default_sobol_table; 111 112 /** @endcond */ 113 114 //!Instantiations of class template sobol_engine model a \quasi_random_number_generator. 115 //!The sobol_engine uses the algorithm described in 116 //! \blockquote 117 //![Bratley+Fox, TOMS 14, 88 (1988)] 118 //!and [Antonov+Saleev, USSR Comput. Maths. Math. Phys. 19, 252 (1980)] 119 //! \endblockquote 120 //! 121 //!\attention sobol_engine skips trivial zeroes at the start of the sequence. For example, the beginning 122 //!of the 2-dimensional Sobol sequence in @c uniform_01 distribution will look like this: 123 //!\code{.cpp} 124 //!0.5, 0.5, 125 //!0.75, 0.25, 126 //!0.25, 0.75, 127 //!0.375, 0.375, 128 //!0.875, 0.875, 129 //!... 130 //!\endcode 131 //! 132 //!In the following documentation @c X denotes the concrete class of the template 133 //!sobol_engine returning objects of type @c UIntType, u and v are the values of @c X. 134 //! 135 //!Some member functions may throw exceptions of type @c std::range_error. This 136 //!happens when the quasi-random domain is exhausted and the generator cannot produce 137 //!any more values. The length of the low discrepancy sequence is given by \f$L=Dimension \times (2^{w} - 1)\f$. 138 template<typename UIntType, unsigned w, typename SobolTables = default_sobol_table> 139 class sobol_engine 140 : public qrng_detail::gray_coded_qrng< 141 qrng_detail::sobol_lattice<UIntType, w, SobolTables> 142 > 143 { 144 typedef qrng_detail::sobol_lattice<UIntType, w, SobolTables> lattice_t; 145 typedef qrng_detail::gray_coded_qrng<lattice_t> base_t; 146 147 public: 148 //!Effects: Constructs the default `s`-dimensional Sobol quasi-random number generator. 149 //! 150 //!Throws: bad_alloc, invalid_argument, range_error. sobol_engine(std::size_t s)151 explicit sobol_engine(std::size_t s) 152 : base_t(s) 153 {} 154 155 // default copy c-tor is fine 156 157 #ifdef BOOST_RANDOM_DOXYGEN 158 //=========================Doxygen needs this!============================== 159 typedef UIntType result_type; 160 161 /** @copydoc boost::random::niederreiter_base2_engine::min() */ BOOST_PREVENT_MACRO_SUBSTITUTION()162 static BOOST_CONSTEXPR result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () 163 { return (base_t::min)(); } 164 165 /** @copydoc boost::random::niederreiter_base2_engine::max() */ BOOST_PREVENT_MACRO_SUBSTITUTION()166 static BOOST_CONSTEXPR result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () 167 { return (base_t::max)(); } 168 169 /** @copydoc boost::random::niederreiter_base2_engine::dimension() */ dimension() const170 std::size_t dimension() const { return base_t::dimension(); } 171 172 /** @copydoc boost::random::niederreiter_base2_engine::seed() */ seed()173 void seed() 174 { 175 base_t::seed(); 176 } 177 178 /** @copydoc boost::random::niederreiter_base2_engine::seed(UIntType) */ seed(UIntType init)179 void seed(UIntType init) 180 { 181 base_t::seed(init); 182 } 183 184 /** @copydoc boost::random::niederreiter_base2_engine::operator()() */ operator ()()185 result_type operator()() 186 { 187 return base_t::operator()(); 188 } 189 190 /** @copydoc boost::random::niederreiter_base2_engine::discard(boost::uintmax_t) */ discard(boost::uintmax_t z)191 void discard(boost::uintmax_t z) 192 { 193 base_t::discard(z); 194 } 195 196 /** Returns true if the two generators will produce identical sequences of outputs. */ BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(sobol_engine,x,y)197 BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(sobol_engine, x, y) 198 { return static_cast<const base_t&>(x) == y; } 199 200 /** Returns true if the two generators will produce different sequences of outputs. */ 201 BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(sobol_engine) 202 203 /** Writes the textual representation of the generator to a @c std::ostream. */ BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os,sobol_engine,s)204 BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, sobol_engine, s) 205 { return os << static_cast<const base_t&>(s); } 206 207 /** Reads the textual representation of the generator from a @c std::istream. */ BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is,sobol_engine,s)208 BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, sobol_engine, s) 209 { return is >> static_cast<base_t&>(s); } 210 211 #endif // BOOST_RANDOM_DOXYGEN 212 }; 213 214 /** 215 * @attention This specialization of \sobol_engine supports up to 3667 dimensions. 216 * 217 * Data on the primitive binary polynomials `a` and the corresponding starting values `m` 218 * for Sobol sequences in up to 21201 dimensions was taken from 219 * 220 * @blockquote 221 * S. Joe and F. Y. Kuo, Constructing Sobol sequences with better two-dimensional projections, 222 * SIAM J. Sci. Comput. 30, 2635-2654 (2008). 223 * @endblockquote 224 * 225 * See the original tables up to dimension 21201: https://web.archive.org/web/20170802022909/http://web.maths.unsw.edu.au/~fkuo/sobol/new-joe-kuo-6.21201 226 * 227 * For practical reasons the default table uses only the subset of binary polynomials `a` < 2<sup>16</sup>. 228 * 229 * However, it is possible to provide your own table to \sobol_engine should the default one be insufficient. 230 */ 231 typedef sobol_engine<boost::uint_least64_t, 64u, default_sobol_table> sobol; 232 233 } // namespace random 234 235 } // namespace boost 236 237 #endif // BOOST_RANDOM_SOBOL_HPP 238