linear_congruential_engine.hpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. //---------------------------------------------------------------------------//
  2. // Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
  3. //
  4. // Distributed under the Boost Software License, Version 1.0
  5. // See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt
  7. //
  8. // See http://boostorg.github.com/compute for more information.
  9. //---------------------------------------------------------------------------//
  10. #ifndef BOOST_COMPUTE_RANDOM_LINEAR_CONGRUENTIAL_ENGINE_HPP
  11. #define BOOST_COMPUTE_RANDOM_LINEAR_CONGRUENTIAL_ENGINE_HPP
  12. #include <algorithm>
  13. #include <boost/compute/types.hpp>
  14. #include <boost/compute/buffer.hpp>
  15. #include <boost/compute/kernel.hpp>
  16. #include <boost/compute/context.hpp>
  17. #include <boost/compute/program.hpp>
  18. #include <boost/compute/command_queue.hpp>
  19. #include <boost/compute/algorithm/transform.hpp>
  20. #include <boost/compute/container/vector.hpp>
  21. #include <boost/compute/detail/iterator_range_size.hpp>
  22. #include <boost/compute/iterator/discard_iterator.hpp>
  23. #include <boost/compute/utility/program_cache.hpp>
  24. namespace boost {
  25. namespace compute {
  26. ///
  27. /// \class linear_congruential_engine
  28. /// \brief 'Quick and Dirty' linear congruential engine
  29. ///
  30. /// Quick and dirty linear congruential engine to generate low quality
  31. /// random numbers very quickly. For uses in which good quality of random
  32. /// numbers is required(Monte-Carlo Simulations), use other engines like
  33. /// Mersenne Twister instead.
  34. ///
  35. template<class T = uint_>
  36. class linear_congruential_engine
  37. {
  38. public:
  39. typedef T result_type;
  40. static const T default_seed = 1;
  41. static const T a = 1099087573;
  42. static const size_t threads = 1024;
  43. /// Creates a new linear_congruential_engine and seeds it with \p value.
  44. explicit linear_congruential_engine(command_queue &queue,
  45. result_type value = default_seed)
  46. : m_context(queue.get_context()),
  47. m_multiplicands(m_context, threads * sizeof(result_type))
  48. {
  49. // setup program
  50. load_program();
  51. // seed state
  52. seed(value, queue);
  53. // generate multiplicands
  54. generate_multiplicands(queue);
  55. }
  56. /// Creates a new linear_congruential_engine object as a copy of \p other.
  57. linear_congruential_engine(const linear_congruential_engine<T> &other)
  58. : m_context(other.m_context),
  59. m_program(other.m_program),
  60. m_seed(other.m_seed),
  61. m_multiplicands(other.m_multiplicands)
  62. {
  63. }
  64. /// Copies \p other to \c *this.
  65. linear_congruential_engine<T>&
  66. operator=(const linear_congruential_engine<T> &other)
  67. {
  68. if(this != &other){
  69. m_context = other.m_context;
  70. m_program = other.m_program;
  71. m_seed = other.m_seed;
  72. m_multiplicands = other.m_multiplicands;
  73. }
  74. return *this;
  75. }
  76. /// Destroys the linear_congruential_engine object.
  77. ~linear_congruential_engine()
  78. {
  79. }
  80. /// Seeds the random number generator with \p value.
  81. ///
  82. /// \param value seed value for the random-number generator
  83. /// \param queue command queue to perform the operation
  84. ///
  85. /// If no seed value is provided, \c default_seed is used.
  86. void seed(result_type value, command_queue &queue)
  87. {
  88. (void) queue;
  89. m_seed = value;
  90. }
  91. /// \overload
  92. void seed(command_queue &queue)
  93. {
  94. seed(default_seed, queue);
  95. }
  96. /// Generates random numbers and stores them to the range [\p first, \p last).
  97. template<class OutputIterator>
  98. void generate(OutputIterator first, OutputIterator last, command_queue &queue)
  99. {
  100. size_t size = detail::iterator_range_size(first, last);
  101. kernel fill_kernel(m_program, "fill");
  102. fill_kernel.set_arg(1, m_multiplicands);
  103. fill_kernel.set_arg(2, first.get_buffer());
  104. size_t offset = 0;
  105. for(;;){
  106. size_t count = 0;
  107. if(size > threads){
  108. count = (std::min)(static_cast<size_t>(threads), size - offset);
  109. }
  110. else {
  111. count = size;
  112. }
  113. fill_kernel.set_arg(0, static_cast<const uint_>(m_seed));
  114. fill_kernel.set_arg(3, static_cast<const uint_>(offset));
  115. queue.enqueue_1d_range_kernel(fill_kernel, 0, count, 0);
  116. offset += count;
  117. if(offset >= size){
  118. break;
  119. }
  120. update_seed(queue);
  121. }
  122. }
  123. /// \internal_
  124. void generate(discard_iterator first, discard_iterator last, command_queue &queue)
  125. {
  126. (void) queue;
  127. size_t size = detail::iterator_range_size(first, last);
  128. uint_ max_mult =
  129. detail::read_single_value<T>(m_multiplicands, threads-1, queue);
  130. while(size >= threads) {
  131. m_seed *= max_mult;
  132. size -= threads;
  133. }
  134. m_seed *=
  135. detail::read_single_value<T>(m_multiplicands, size-1, queue);
  136. }
  137. /// Generates random numbers, transforms them with \p op, and then stores
  138. /// them to the range [\p first, \p last).
  139. template<class OutputIterator, class Function>
  140. void generate(OutputIterator first, OutputIterator last, Function op, command_queue &queue)
  141. {
  142. vector<T> tmp(std::distance(first, last), queue.get_context());
  143. generate(tmp.begin(), tmp.end(), queue);
  144. transform(tmp.begin(), tmp.end(), first, op, queue);
  145. }
  146. /// Generates \p z random numbers and discards them.
  147. void discard(size_t z, command_queue &queue)
  148. {
  149. generate(discard_iterator(0), discard_iterator(z), queue);
  150. }
  151. private:
  152. /// \internal_
  153. /// Generates the multiplicands for each thread
  154. void generate_multiplicands(command_queue &queue)
  155. {
  156. kernel multiplicand_kernel =
  157. m_program.create_kernel("multiplicand");
  158. multiplicand_kernel.set_arg(0, m_multiplicands);
  159. queue.enqueue_task(multiplicand_kernel);
  160. }
  161. /// \internal_
  162. void update_seed(command_queue &queue)
  163. {
  164. m_seed *=
  165. detail::read_single_value<T>(m_multiplicands, threads-1, queue);
  166. }
  167. /// \internal_
  168. void load_program()
  169. {
  170. boost::shared_ptr<program_cache> cache =
  171. program_cache::get_global_cache(m_context);
  172. std::string cache_key =
  173. std::string("__boost_linear_congruential_engine_") + type_name<T>();
  174. const char source[] =
  175. "__kernel void multiplicand(__global uint *multiplicands)\n"
  176. "{\n"
  177. " uint a = 1099087573;\n"
  178. " multiplicands[0] = a;\n"
  179. " for(uint i = 1; i < 1024; i++){\n"
  180. " multiplicands[i] = a * multiplicands[i-1];\n"
  181. " }\n"
  182. "}\n"
  183. "__kernel void fill(const uint seed,\n"
  184. " __global uint *multiplicands,\n"
  185. " __global uint *result,"
  186. " const uint offset)\n"
  187. "{\n"
  188. " const uint i = get_global_id(0);\n"
  189. " result[offset+i] = seed * multiplicands[i];\n"
  190. "}\n";
  191. m_program = cache->get_or_build(cache_key, std::string(), source, m_context);
  192. }
  193. private:
  194. context m_context;
  195. program m_program;
  196. T m_seed;
  197. buffer m_multiplicands;
  198. };
  199. } // end compute namespace
  200. } // end boost namespace
  201. #endif // BOOST_COMPUTE_RANDOM_LINEAR_CONGRUENTIAL_ENGINE_HPP