mixmax.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /* boost random/mixmax.hpp header file
  2. *
  3. * Copyright Kostas Savvidis 2008-2019
  4. *
  5. * Distributed under the Boost Software License, Version 1.0. (See
  6. * accompanying file LICENSE_1_0.txt or copy at
  7. * http://www.boost.org/LICENSE_1_0.txt)
  8. *
  9. * See http://www.boost.org for most recent version including documentation.
  10. *
  11. * $Id$
  12. *
  13. * Revision history
  14. * 2019-04-23 created
  15. */
  16. #ifndef BOOST_RANDOM_MIXMAX_HPP
  17. #define BOOST_RANDOM_MIXMAX_HPP
  18. #include <sstream>
  19. #include <boost/cstdint.hpp>
  20. #include <boost/array.hpp>
  21. #include <boost/random/detail/seed.hpp>
  22. #include <boost/random/detail/seed_impl.hpp>
  23. namespace boost {
  24. namespace random {
  25. /**
  26. * Instantiations of class template mixmax_engine model,
  27. * \pseudo_random_number_generator .
  28. * It uses the MIXMAX generator algorithms from:
  29. *
  30. * @blockquote
  31. * G.K.Savvidy and N.G.Ter-Arutyunian,
  32. * On the Monte Carlo simulation of physical systems,
  33. * J.Comput.Phys. 97, 566 (1991);
  34. * Preprint EPI-865-16-86, Yerevan, Jan. 1986
  35. * http://dx.doi.org/10.1016/0021-9991(91)90015-D
  36. *
  37. * K.Savvidy
  38. * The MIXMAX random number generator
  39. * Comp. Phys. Commun. 196 (2015), pp 161–165
  40. * http://dx.doi.org/10.1016/j.cpc.2015.06.003
  41. *
  42. * K.Savvidy and G.Savvidy
  43. * Spectrum and Entropy of C-systems. MIXMAX random number generator
  44. * Chaos, Solitons & Fractals, Volume 91, (2016) pp. 33–38
  45. * http://dx.doi.org/10.1016/j.chaos.2016.05.003
  46. * @endblockquote
  47. *
  48. * The generator crucially depends on the choice of the
  49. * parameters. The valid sets of parameters are from the published papers above.
  50. *
  51. */
  52. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> // MIXMAX TEMPLATE PARAMETERS
  53. class mixmax_engine{
  54. public:
  55. // Interfaces required by C++11 std::random and boost::random
  56. typedef boost::uint64_t result_type ;
  57. BOOST_STATIC_CONSTANT(boost::uint64_t,mixmax_min=0);
  58. BOOST_STATIC_CONSTANT(boost::uint64_t,mixmax_max=((1ULL<<61)-1));
  59. BOOST_STATIC_CONSTEXPR result_type min BOOST_PREVENT_MACRO_SUBSTITUTION() {return mixmax_min;}
  60. BOOST_STATIC_CONSTEXPR result_type max BOOST_PREVENT_MACRO_SUBSTITUTION() {return mixmax_max;}
  61. static const bool has_fixed_range = false;
  62. BOOST_STATIC_CONSTANT(int,N=Ndim); ///< The main internal parameter, size of the defining MIXMAX matrix
  63. // CONSTRUCTORS:
  64. explicit mixmax_engine(); ///< Constructor, unit vector as initial state, acted on by A^2^512
  65. explicit mixmax_engine(boost::uint64_t); ///< Constructor, one 64-bit seed
  66. explicit mixmax_engine(uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t streamID ); ///< Constructor, four 32-bit seeds for 128-bit seeding flexibility
  67. void seed(boost::uint64_t seedval=default_seed){seed_uniquestream( &S, 0, 0, (uint32_t)(seedval>>32), (uint32_t)seedval );} ///< seed with one 64-bit seed
  68. private: // DATATYPES
  69. struct rng_state_st{
  70. boost::array<boost::uint64_t, Ndim> V;
  71. boost::uint64_t sumtot;
  72. int counter;
  73. };
  74. typedef struct rng_state_st rng_state_t; // struct alias
  75. rng_state_t S;
  76. public: // SEEDING FUNCTIONS
  77. template<class It> mixmax_engine(It& first, It last) { seed(first,last); }
  78. BOOST_RANDOM_DETAIL_SEED_SEQ_CONSTRUCTOR(mixmax_engine, SeedSeq, seq){ seed(seq); }
  79. /** Sets the state of the generator using values from an iterator range. */
  80. template<class It>
  81. void seed(It& first, It last){
  82. uint32_t v[4];
  83. detail::fill_array_int<32>(first, last, v);
  84. seed_uniquestream( &S, v[0], v[1], v[2], v[3]);
  85. }
  86. /** Sets the state of the generator using values from a seed_seq. */
  87. BOOST_RANDOM_DETAIL_SEED_SEQ_SEED(mixmax_engine, SeeqSeq, seq){
  88. uint32_t v[4];
  89. detail::seed_array_int<32>(seq, v);
  90. seed_uniquestream( &S, v[0], v[1], v[2], v[3]);
  91. }
  92. /** return one uint64 between min=0 and max=2^61-1 */
  93. boost::uint64_t operator()(){
  94. if (S.counter<=(Ndim-1) ){
  95. return S.V[S.counter++];
  96. }else{
  97. S.sumtot = iterate_raw_vec(S.V.data(), S.sumtot);
  98. S.counter=2;
  99. return S.V[1];
  100. }
  101. }
  102. /** Fills a range with random values */
  103. template<class Iter>
  104. void generate(Iter first, Iter last) { detail::generate_from_int(*this, first, last); }
  105. void discard(boost::uint64_t nsteps) { for(boost::uint64_t j = 0; j < nsteps; ++j) (*this)(); } ///< discard n steps, required in boost::random
  106. /** save the state of the RNG to a stream */
  107. template<class CharT, class Traits>
  108. friend std::basic_ostream<CharT,Traits>&
  109. operator<< (std::basic_ostream<CharT,Traits>& ost, const mixmax_engine& me){
  110. ost << Ndim << " " << me.S.counter << " " << me.S.sumtot << " ";
  111. for (int j=0; (j< (Ndim) ); j++) {
  112. ost << (boost::uint64_t)me.S.V[j] << " ";
  113. }
  114. ost << "\n";
  115. ost.flush();
  116. return ost;
  117. }
  118. /** read the state of the RNG from a stream */
  119. template<class CharT, class Traits>
  120. friend std::basic_istream<CharT,Traits>&
  121. operator>> (std::basic_istream<CharT,Traits> &in, mixmax_engine& me){
  122. // will set std::ios::failbit if the input format is not right
  123. boost::array<boost::uint64_t, Ndim> vec;
  124. boost::uint64_t sum=0, savedsum=0, counter=0;
  125. in >> counter >> std::ws;
  126. BOOST_ASSERT(counter==Ndim);
  127. in >> counter >> std::ws;
  128. in >> savedsum >> std::ws;
  129. for(int j=0;j<Ndim;j++) {
  130. in >> std::ws >> vec[j] ;
  131. sum=me.MOD_MERSENNE(sum+vec[j]);
  132. }
  133. if (sum == savedsum && counter>0 && counter<Ndim){
  134. me.S.V=vec; me.S.counter = counter; me.S.sumtot=savedsum;
  135. }else{
  136. in.setstate(std::ios::failbit);
  137. }
  138. return in;
  139. }
  140. friend bool operator==(const mixmax_engine & x,
  141. const mixmax_engine & y){return x.S.counter==y.S.counter && x.S.sumtot==y.S.sumtot && x.S.V==y.S.V ;}
  142. friend bool operator!=(const mixmax_engine & x,
  143. const mixmax_engine & y){return !operator==(x,y);}
  144. private:
  145. BOOST_STATIC_CONSTANT(int, BITS=61);
  146. BOOST_STATIC_CONSTANT(boost::uint64_t, M61=2305843009213693951ULL);
  147. BOOST_STATIC_CONSTANT(boost::uint64_t, default_seed=1);
  148. inline boost::uint64_t MOD_MERSENNE(boost::uint64_t k) {return ((((k)) & M61) + (((k)) >> BITS) );}
  149. inline boost::uint64_t MULWU(boost::uint64_t k);
  150. inline void seed_vielbein(rng_state_t* X, unsigned int i); // seeds with the i-th unit vector, i = 0..Ndim-1, for testing only
  151. inline void seed_uniquestream( rng_state_t* Xin, uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t streamID );
  152. inline boost::uint64_t iterate_raw_vec(boost::uint64_t* Y, boost::uint64_t sumtotOld);
  153. inline boost::uint64_t apply_bigskip(boost::uint64_t* Vout, boost::uint64_t* Vin, uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t streamID );
  154. inline boost::uint64_t modadd(boost::uint64_t foo, boost::uint64_t bar);
  155. inline boost::uint64_t fmodmulM61(boost::uint64_t cum, boost::uint64_t s, boost::uint64_t a);
  156. };
  157. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::mixmax_engine()
  158. ///< constructor, with no params, seeds with seed=0, random numbers are as good as from any other seed
  159. {
  160. seed_uniquestream( &S, 0, 0, 0, default_seed);
  161. }
  162. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::mixmax_engine(boost::uint64_t seedval){
  163. ///< constructor, one uint64_t seed, random numbers are statistically independent from any two distinct seeds, e.g. consecutive seeds are ok
  164. seed_uniquestream( &S, 0, 0, (uint32_t)(seedval>>32), (uint32_t)seedval );
  165. }
  166. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::mixmax_engine(uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t streamID){
  167. // constructor, four 32-bit seeds for 128-bit seeding flexibility
  168. seed_uniquestream( &S, clusterID, machineID, runID, streamID );
  169. }
  170. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> uint64_t mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::MULWU (uint64_t k){ return (( (k)<<(SPECIALMUL) & M61) ^ ( (k) >> (BITS-SPECIALMUL)) ) ;}
  171. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> boost::uint64_t mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::iterate_raw_vec(boost::uint64_t* Y, boost::uint64_t sumtotOld){
  172. // operates with a raw vector, uses known sum of elements of Y
  173. boost::uint64_t tempP=0, tempV=sumtotOld;
  174. Y[0] = tempV;
  175. boost::uint64_t sumtot = Y[0], ovflow = 0; // will keep a running sum of all new elements
  176. for (int i=1; i<Ndim; i++){
  177. boost::uint64_t tempPO = MULWU(tempP);
  178. tempV = (tempV+tempPO);
  179. tempP = modadd(tempP, Y[i]);
  180. tempV = modadd(tempV, tempP); // new Y[i] = old Y[i] + old partial * m
  181. Y[i] = tempV;
  182. sumtot += tempV; if (sumtot < tempV) {ovflow++;}
  183. }
  184. return MOD_MERSENNE(MOD_MERSENNE(sumtot) + (ovflow <<3 ));
  185. }
  186. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> void mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::seed_vielbein(rng_state_t* X, unsigned int index){
  187. for (int i=0; i < Ndim; i++){
  188. X->V[i] = 0;
  189. }
  190. if (index<Ndim) { X->V[index] = 1; }else{ X->V[0]=1; }
  191. X->counter = Ndim; // set the counter to Ndim if iteration should happen right away
  192. X->sumtot = 1;
  193. }
  194. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> void mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::seed_uniquestream( rng_state_t* Xin, uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t streamID ){
  195. seed_vielbein(Xin,0);
  196. Xin->sumtot = apply_bigskip(Xin->V.data(), Xin->V.data(), clusterID, machineID, runID, streamID );
  197. Xin->counter = 1;
  198. }
  199. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> boost::uint64_t mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::apply_bigskip( boost::uint64_t* Vout, boost::uint64_t* Vin, uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t streamID ){
  200. /*
  201. makes a derived state vector, Vout, from the mother state vector Vin
  202. by skipping a large number of steps, determined by the given seeding ID's
  203. it is mathematically guaranteed that the substreams derived in this way from the SAME (!!!) Vin will not collide provided
  204. 1) at least one bit of ID is different
  205. 2) less than 10^100 numbers are drawn from the stream
  206. (this is good enough : a single CPU will not exceed this in the lifetime of the universe, 10^19 sec,
  207. even if it had a clock cycle of Planck time, 10^44 Hz )
  208. Caution: never apply this to a derived vector, just choose some mother vector Vin, for example the unit vector by seed_vielbein(X,0),
  209. and use it in all your runs, just change runID to get completely nonoverlapping streams of random numbers on a different day.
  210. clusterID and machineID are provided for the benefit of large organizations who wish to ensure that a simulation
  211. which is running in parallel on a large number of clusters and machines will have non-colliding source of random numbers.
  212. did i repeat it enough times? the non-collision guarantee is absolute, not probabilistic
  213. */
  214. const boost::uint64_t skipMat17[128][17] =
  215. #include "boost/random/detail/mixmax_skip_N17.ipp"
  216. ;
  217. const boost::uint64_t* skipMat[128];
  218. BOOST_ASSERT(Ndim==17);
  219. for (int i=0; i<128; i++) { skipMat[i] = skipMat17[i];}
  220. uint32_t IDvec[4] = {streamID, runID, machineID, clusterID};
  221. boost::uint64_t Y[Ndim], cum[Ndim];
  222. boost::uint64_t sumtot=0;
  223. for (int i=0; i<Ndim; i++) { Y[i] = Vin[i]; sumtot = modadd( sumtot, Vin[i]); } ;
  224. for (int IDindex=0; IDindex<4; IDindex++) { // go from lower order to higher order ID
  225. uint32_t id=IDvec[IDindex];
  226. int r = 0;
  227. while (id){
  228. if (id & 1) {
  229. boost::uint64_t* rowPtr = (boost::uint64_t*)skipMat[r + IDindex*8*sizeof(uint32_t)];
  230. for (int i=0; i<Ndim; i++){ cum[i] = 0; }
  231. for (int j=0; j<Ndim; j++){ // j is lag, enumerates terms of the poly
  232. // for zero lag Y is already given
  233. boost::uint64_t coeff = rowPtr[j]; // same coeff for all i
  234. for (int i =0; i<Ndim; i++){
  235. cum[i] = fmodmulM61( cum[i], coeff , Y[i] ) ;
  236. }
  237. sumtot = iterate_raw_vec(Y, sumtot);
  238. }
  239. sumtot=0;
  240. for (int i=0; i<Ndim; i++){ Y[i] = cum[i]; sumtot = modadd( sumtot, cum[i]); } ;
  241. }
  242. id = (id >> 1); r++; // bring up the r-th bit in the ID
  243. }
  244. }
  245. sumtot=0;
  246. for (int i=0; i<Ndim; i++){ Vout[i] = Y[i]; sumtot = modadd( sumtot, Y[i]); } ; // returns sumtot, and copy the vector over to Vout
  247. return (sumtot) ;
  248. }
  249. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> inline boost::uint64_t mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::fmodmulM61(boost::uint64_t cum, boost::uint64_t s, boost::uint64_t a){
  250. // works on all platforms, including 32-bit linux, PPC and PPC64, ARM and Windows
  251. const boost::uint64_t MASK32=0xFFFFFFFFULL;
  252. boost::uint64_t o,ph,pl,ah,al;
  253. o=(s)*a;
  254. ph = ((s)>>32);
  255. pl = (s) & MASK32;
  256. ah = a>>32;
  257. al = a & MASK32;
  258. o = (o & M61) + ((ph*ah)<<3) + ((ah*pl+al*ph + ((al*pl)>>32))>>29) ;
  259. o += cum;
  260. o = (o & M61) + ((o>>61));
  261. return o;
  262. }
  263. template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> boost::uint64_t mixmax_engine <Ndim, SPECIALMUL, SPECIAL> ::modadd(boost::uint64_t foo, boost::uint64_t bar){
  264. return MOD_MERSENNE(foo+bar);
  265. }
  266. /* @copydoc boost::random::detail::mixmax_engine_doc */
  267. /** Instantiation with a valid parameter set. */
  268. typedef mixmax_engine<17,36,0> mixmax;
  269. }// namespace random
  270. }// namespace boost
  271. #endif // BOOST_RANDOM_MIXMAX_HPP