1 //
2 // -*- C++ -*-
3 //
4 // -----------------------------------------------------------------------
5 // HEP Random
6 // --- MixMaxRng ---
7 // class implementation file
8 // -----------------------------------------------------------------------
9 //
10 // This file interfaces the MixMax PseudoRandom Number Generator
11 // proposed by:
12 //
13 // G.K.Savvidy and N.G.Ter-Arutyunian,
14 // On the Monte Carlo simulation of physical systems,
15 // J.Comput.Phys. 97, 566 (1991);
16 // Preprint EPI-865-16-86, Yerevan, Jan. 1986
17 // http://dx.doi.org/10.1016/0021-9991(91)90015-D
18 //
19 // K.Savvidy
20 // "The MIXMAX random number generator"
21 // Comp. Phys. Commun. (2015)
22 // http://dx.doi.org/10.1016/j.cpc.2015.06.003
23 //
24 // K.Savvidy and G.Savvidy
25 // "Spectrum and Entropy of C-systems. MIXMAX random number generator"
26 // Chaos, Solitons & Fractals, Volume 91, (2016) pp. 33-38
27 // http://dx.doi.org/10.1016/j.chaos.2016.05.003
28 //
29 // =======================================================================
30 // Implementation by Konstantin Savvidy - Copyright 2004-2017
31 // =======================================================================
32
33 #include "CLHEP/Random/defs.h"
34 #include "CLHEP/Random/Random.h"
35 #include "CLHEP/Random/MixMaxRng.h"
36 #include "CLHEP/Random/engineIDulong.h"
37 #include "CLHEP/Utility/atomic_int.h"
38
39 #include <string.h> // for strcmp
40 #include <cmath>
41
42 const unsigned long MASK32=0xffffffff;
43
44 namespace CLHEP {
45
46 namespace {
47 // Number of instances with automatic seed selection
48 CLHEP_ATOMIC_INT_TYPE numberOfEngines(0);
49 }
50
51 static const int MarkerLen = 64; // Enough room to hold a begin or end marker.
52
MixMaxRng()53 MixMaxRng::MixMaxRng()
54 : HepRandomEngine()
55 {
56 int numEngines = ++numberOfEngines;
57 setSeed(static_cast<long>(numEngines));
58 }
59
MixMaxRng(long seed)60 MixMaxRng::MixMaxRng(long seed)
61 : HepRandomEngine()
62 {
63 theSeed=seed;
64 setSeed(seed);
65 }
66
MixMaxRng(std::istream & is)67 MixMaxRng::MixMaxRng(std::istream& is)
68 : HepRandomEngine()
69 {
70 get(is);
71 }
72
~MixMaxRng()73 MixMaxRng::~MixMaxRng()
74 {
75 }
76
MixMaxRng(const MixMaxRng & rng)77 MixMaxRng::MixMaxRng(const MixMaxRng& rng)
78 : HepRandomEngine(rng)
79 {
80 S.V = rng.S.V;
81 S.sumtot= rng.S.sumtot;
82 S.counter= rng.S.counter;
83 }
84
operator =(const MixMaxRng & rng)85 MixMaxRng& MixMaxRng::operator=(const MixMaxRng& rng)
86 {
87 // Check assignment to self
88 //
89 if (this == &rng) { return *this; }
90
91 // Copy base class data
92 //
93 HepRandomEngine::operator=(rng);
94
95 S.V = rng.S.V;
96 S.sumtot= rng.S.sumtot;
97 S.counter= rng.S.counter;
98
99 return *this;
100 }
101
saveStatus(const char filename[]) const102 void MixMaxRng::saveStatus( const char filename[] ) const
103 {
104 // Create a C file-handle or an object that can accept the same output
105 FILE *fh= fopen(filename, "w");
106 if( fh )
107 {
108 int j;
109 fprintf(fh, "mixmax state, file version 1.0\n" );
110 fprintf(fh, "N=%u; V[N]={", rng_get_N() );
111 for (j=0; (j< (rng_get_N()-1) ); j++) {
112 fprintf(fh, "%llu, ", S.V[j] );
113 }
114 fprintf(fh, "%llu", S.V[rng_get_N()-1] );
115 fprintf(fh, "}; " );
116 fprintf(fh, "counter=%u; ", S.counter );
117 fprintf(fh, "sumtot=%llu;\n", S.sumtot );
118 fclose(fh);
119 }
120 }
121
122 #define MIXMAX_ARRAY_INDEX_OUT_OF_BOUNDS 0xFF01
123 #define MIXMAX_SEED_WAS_ZERO 0xFF02
124 #define MIXMAX_ERROR_READING_STATE_FILE 0xFF03
125 #define MIXMAX_ERROR_READING_STATE_COUNTER 0xFF04
126 #define MIXMAX_ERROR_READING_STATE_CHECKSUM 0xFF05
127
restoreStatus(const char filename[])128 void MixMaxRng::restoreStatus( const char filename[] )
129 {
130 // a function for reading the state from a file
131 FILE* fin;
132 if( ( fin = fopen(filename, "r") ) )
133 {
134 char l=0;
135 while ( l != '{' ) { // 0x7B = "{"
136 l=fgetc(fin); // proceed until hitting opening bracket
137 }
138 ungetc(' ', fin);
139 }
140 else
141 {
142 fprintf(stderr, "mixmax -> read_state: error reading file %s\n", filename);
143 exit(MIXMAX_ERROR_READING_STATE_FILE);
144 }
145
146 myuint_t vecVal;
147 //printf("mixmax -> read_state: starting to read state from file\n");
148 if (!fscanf(fin, "%llu", &S.V[0]) )
149 {
150 fprintf(stderr, "mixmax -> read_state: error reading file %s\n", filename);
151 exit(MIXMAX_ERROR_READING_STATE_FILE);
152 }
153
154 int i;
155 for( i = 1; i < rng_get_N(); i++)
156 {
157 if (!fscanf(fin, ", %llu", &vecVal) )
158 {
159 fprintf(stderr, "mixmax -> read_state: error reading vector component i=%d from file %s\n", i, filename);
160 exit(MIXMAX_ERROR_READING_STATE_FILE);
161 }
162 if( vecVal <= MixMaxRng::M61 )
163 {
164 S.V[i] = vecVal;
165 }
166 else
167 {
168 fprintf(stderr, "mixmax -> read_state: Invalid state vector value= %llu"
169 " ( must be less than %llu ) "
170 " obtained from reading file %s\n"
171 , vecVal, MixMaxRng::M61, filename);
172 }
173 }
174
175 int counter;
176 if (!fscanf( fin, "}; counter=%i; ", &counter))
177 {
178 fprintf(stderr, "mixmax -> read_state: error reading counter from file %s\n", filename);
179 exit(MIXMAX_ERROR_READING_STATE_FILE);
180 }
181 if( counter <= rng_get_N() )
182 {
183 S.counter= counter;
184 }
185 else
186 {
187 fprintf(stderr, "mixmax -> read_state: Invalid counter = %d"
188 " Must be 0 <= counter < %u\n" , counter, rng_get_N());
189 print_state();
190 exit(MIXMAX_ERROR_READING_STATE_COUNTER);
191 }
192 precalc();
193 myuint_t sumtot;
194 if (!fscanf( fin, "sumtot=%llu\n", &sumtot))
195 {
196 fprintf(stderr, "mixmax -> read_state: error reading checksum from file %s\n", filename);
197 exit(MIXMAX_ERROR_READING_STATE_FILE);
198 }
199
200 if (S.sumtot != sumtot)
201 {
202 fprintf(stderr, "mixmax -> checksum error while reading state from file %s - corrupted?\n", filename);
203 exit(MIXMAX_ERROR_READING_STATE_CHECKSUM);
204 }
205 fclose(fin);
206 }
207
208 #undef MIXMAX_ARRAY_INDEX_OUT_OF_BOUNDS
209 #undef MIXMAX_SEED_WAS_ZERO
210 #undef MIXMAX_ERROR_READING_STATE_FILE
211 #undef MIXMAX_ERROR_READING_STATE_COUNTER
212 #undef MIXMAX_ERROR_READING_STATE_CHECKSUM
213
showStatus() const214 void MixMaxRng::showStatus() const
215 {
216 std::cout << std::endl;
217 std::cout << "------- MixMaxRng engine status -------" << std::endl;
218
219 std::cout << " Current state vector is:" << std::endl;
220 print_state();
221 std::cout << "---------------------------------------" << std::endl;
222 }
223
setSeed(long longSeed,int)224 void MixMaxRng::setSeed(long longSeed, int /* extraSeed */)
225 {
226 //seed_uniquestream(0,0,0,longSeed);
227 theSeed = longSeed;
228 seed_spbox(longSeed);
229 }
230
231 // Preferred Seeding method
232 // the values of 'Seeds' must be valid 32-bit integers
233 // Higher order bits will be ignored!!
234
setSeeds(const long * Seeds,int seedNum)235 void MixMaxRng::setSeeds(const long* Seeds, int seedNum)
236 {
237 unsigned long seed0, seed1= 0, seed2= 0, seed3= 0;
238
239 if( seedNum < 1 ) { // Assuming at least 2 seeds in vector...
240 seed0= static_cast<unsigned long>(Seeds[0]) & MASK32;
241 seed1= static_cast<unsigned long>(Seeds[1]) & MASK32;
242 }
243 else
244 {
245 if( seedNum < 4 ) {
246 seed0= static_cast<unsigned long>(Seeds[0]) & MASK32;
247 if( seedNum > 1){ seed1= static_cast<unsigned long>(Seeds[1]) & MASK32; }
248 if( seedNum > 2){ seed2= static_cast<unsigned long>(Seeds[2]) & MASK32; }
249 }
250 if( seedNum >= 4 ) {
251 seed0= static_cast<unsigned long>(Seeds[0]) & MASK32;
252 seed1= static_cast<unsigned long>(Seeds[1]) & MASK32;
253 seed2= static_cast<unsigned long>(Seeds[2]) & MASK32;
254 seed3= static_cast<unsigned long>(Seeds[3]) & MASK32;
255 }
256 }
257 theSeed = Seeds[0];
258 theSeeds = Seeds;
259 seed_uniquestream(seed3, seed2, seed1, seed0);
260 }
261
engineName()262 std::string MixMaxRng::engineName()
263 {
264 return "MixMaxRng";
265 }
266
rng_get_N()267 constexpr int MixMaxRng::rng_get_N()
268 {
269 return N;
270 }
271
rng_get_SPECIAL()272 constexpr long long int MixMaxRng::rng_get_SPECIAL()
273 {
274 return SPECIAL;
275 }
276
rng_get_SPECIALMUL()277 constexpr int MixMaxRng::rng_get_SPECIALMUL()
278 {
279 return SPECIALMUL;
280 }
281
generate(int i)282 double MixMaxRng::generate(int i)
283 {
284 S.counter++;
285 #if defined(__clang__) || defined(__llvm__)
286 return INV_M61*static_cast<double>(S.V[i]);
287 #elif defined(__GNUC__) && (__GNUC__ < 7) && (!defined(__ICC)) && defined(__x86_64__) && defined(__SSE2_MATH__)
288 int64_t Z=S.V[i];
289 double F=0.0;
290 //#warning Using the inline assembler
291 /* using SSE inline assemly to zero the xmm register, just before int64 -> double conversion,
292 not necessary in GCC-5 or better, but huge penalty on earlier compilers
293 */
294 __asm__ __volatile__( "pxor %0, %0;"
295 "cvtsi2sdq %1, %0;"
296 :"=x"(F)
297 :"r"(Z)
298 );
299 return F*INV_M61;
300 #else
301 //#warning other method
302 return convert1double(S.V[i]); //get_next_float_packbits();
303 #endif
304 }
305
iterate()306 double MixMaxRng::iterate()
307 {
308 myuint_t* Y=S.V.data();
309 myuint_t tempP, tempV;
310 Y[0] = ( tempV = S.sumtot);
311 myuint_t sumtot = Y[0], ovflow = 0; // will keep a running sum of all new elements
312 tempP = 0; // will keep a partial sum of all old elements
313 myuint_t tempPO;
314 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[1] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[1] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
315 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[2] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[2] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
316 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[3] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[3] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
317 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[4] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[4] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
318 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[5] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[5] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
319 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[6] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[6] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
320 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[7] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[7] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
321 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[8] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[8] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
322 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[9] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[9] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
323 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[10]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[10] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
324 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[11]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[11] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
325 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[12]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[12] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
326 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[13]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[13] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
327 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[14]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[14] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
328 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[15]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[15] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
329 tempPO = MULWU(tempP); tempP = modadd(tempP, Y[16]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[16] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
330 S.sumtot = MIXMAX_MOD_MERSENNE(MIXMAX_MOD_MERSENNE(sumtot) + (ovflow <<3 ));
331
332 S.counter=2;
333 return double(S.V[1])*INV_M61;
334 }
335
flatArray(const int size,double * vect)336 void MixMaxRng::flatArray(const int size, double* vect )
337 {
338 // fill_array( S, size, arrayDbl );
339 for (int i=0; i<size; ++i) { vect[i] = flat(); }
340 }
341
operator double()342 MixMaxRng::operator double()
343 {
344 return flat();
345 }
346
operator float()347 MixMaxRng::operator float()
348 {
349 return float( flat() );
350 }
351
operator unsigned int()352 MixMaxRng::operator unsigned int()
353 {
354 return static_cast<unsigned int>(get_next());
355 // clhep_get_next returns a 64-bit integer, of which the lower 61 bits
356 // are random and upper 3 bits are zero
357 }
358
put(std::ostream & os) const359 std::ostream & MixMaxRng::put ( std::ostream& os ) const
360 {
361 char beginMarker[] = "MixMaxRng-begin";
362 char endMarker[] = "MixMaxRng-end";
363
364 int pr = os.precision(24);
365 os << beginMarker << " ";
366 os << theSeed << "\n";
367 for (int i=0; i<rng_get_N(); ++i) {
368 os << S.V[i] << "\n";
369 }
370 os << S.counter << "\n";
371 os << S.sumtot << "\n";
372 os << endMarker << "\n";
373 os.precision(pr);
374 return os;
375 }
376
put() const377 std::vector<unsigned long> MixMaxRng::put () const
378 {
379 std::vector<unsigned long> v;
380 v.push_back (engineIDulong<MixMaxRng>());
381 for (int i=0; i<rng_get_N(); ++i)
382 {
383 v.push_back(static_cast<unsigned long>(S.V[i] & MASK32));
384 // little-ended order on all platforms
385 v.push_back(static_cast<unsigned long>(S.V[i] >> 32 ));
386 // pack uint64 into a data structure which is 32-bit on some platforms
387 }
388 v.push_back(static_cast<unsigned long>(S.counter));
389 v.push_back(static_cast<unsigned long>(S.sumtot & MASK32));
390 v.push_back(static_cast<unsigned long>(S.sumtot >> 32));
391 return v;
392 }
393
get(std::istream & is)394 std::istream & MixMaxRng::get ( std::istream& is)
395 {
396 char beginMarker [MarkerLen];
397 is >> std::ws;
398 is.width(MarkerLen); // causes the next read to the char* to be <=
399 // that many bytes, INCLUDING A TERMINATION \0
400 // (Stroustrup, section 21.3.2)
401 is >> beginMarker;
402 if (strcmp(beginMarker,"MixMaxRng-begin")) {
403 is.clear(std::ios::badbit | is.rdstate());
404 std::cerr << "\nInput stream mispositioned or"
405 << "\nMixMaxRng state description missing or"
406 << "\nwrong engine type found." << std::endl;
407 return is;
408 }
409 return getState(is);
410 }
411
beginTag()412 std::string MixMaxRng::beginTag ()
413 {
414 return "MixMaxRng-begin";
415 }
416
getState(std::istream & is)417 std::istream & MixMaxRng::getState ( std::istream& is )
418 {
419 char endMarker[MarkerLen];
420 is >> theSeed;
421 for (int i=0; i<rng_get_N(); ++i) is >> S.V[i];
422 is >> S.counter;
423 myuint_t checksum;
424 is >> checksum;
425 is >> std::ws;
426 is.width(MarkerLen);
427 is >> endMarker;
428 if (strcmp(endMarker,"MixMaxRng-end")) {
429 is.clear(std::ios::badbit | is.rdstate());
430 std::cerr << "\nMixMaxRng state description incomplete."
431 << "\nInput stream is probably mispositioned now.\n";
432 return is;
433 }
434 if ( S.counter < 0 || S.counter > rng_get_N() ) {
435 std::cerr << "\nMixMaxRng::getState(): "
436 << "vector read wrong value of counter from file!"
437 << "\nInput stream is probably mispositioned now.\n";
438 return is;
439 }
440 precalc();
441 if ( checksum != S.sumtot) {
442 std::cerr << "\nMixMaxRng::getState(): "
443 << "checksum disagrees with value stored in file!"
444 << "\nInput stream is probably mispositioned now.\n";
445 return is;
446 }
447 return is;
448 }
449
get(const std::vector<unsigned long> & v)450 bool MixMaxRng::get (const std::vector<unsigned long> & v)
451 {
452 if ((v[0] & 0xffffffffUL) != engineIDulong<MixMaxRng>()) {
453 std::cerr <<
454 "\nMixMaxRng::get(): vector has wrong ID word - state unchanged\n";
455 return false;
456 }
457 return getState(v);
458 }
459
getState(const std::vector<unsigned long> & v)460 bool MixMaxRng::getState (const std::vector<unsigned long> & v)
461 {
462 if (v.size() != VECTOR_STATE_SIZE ) {
463 std::cerr <<
464 "\nMixMaxRng::getState(): vector has wrong length - state unchanged\n";
465 return false;
466 }
467 for (int i=1; i<2*rng_get_N() ; i=i+2) {
468 S.V[i/2]= ( (v[i] & MASK32) | ( (myuint_t)(v[i+1]) << 32 ) );
469 // unpack from a data structure which is 32-bit on some platforms
470 }
471 S.counter = v[2*rng_get_N()+1];
472 precalc();
473 if ( ( (v[2*rng_get_N()+2] & MASK32)
474 | ( (myuint_t)(v[2*rng_get_N()+3]) << 32 ) ) != S.sumtot) {
475 std::cerr << "\nMixMaxRng::getState(): vector has wrong checksum!"
476 << "\nInput vector is probably mispositioned now.\n";
477 return false;
478 }
479 return true;
480 }
481
MOD_MULSPEC(myuint_t k)482 myuint_t MixMaxRng ::MOD_MULSPEC(myuint_t k)
483 {
484 switch (N)
485 {
486 case 17:
487 return 0;
488 break;
489 case 8:
490 return 0;
491 break;
492 case 240:
493 return fmodmulM61( 0, SPECIAL , (k) );
494 break;
495 default:
496 std::cerr << "MIXMAX ERROR: " << "Disallowed value of parameter N\n";
497 std::terminate();
498 break;
499 }
500 }
501
MULWU(myuint_t k)502 myuint_t MixMaxRng::MULWU (myuint_t k)
503 {
504 return (( (k)<<(SPECIALMUL) & M61) ^ ( (k) >> (BITS-SPECIALMUL)) );
505 }
506
iterate_raw_vec(myuint_t * Y,myuint_t sumtotOld)507 myuint_t MixMaxRng::iterate_raw_vec(myuint_t* Y, myuint_t sumtotOld)
508 {
509 // operates with a raw vector, uses known sum of elements of Y
510 int i;
511
512 myuint_t tempP, tempV;
513 Y[0] = ( tempV = sumtotOld);
514 myuint_t sumtot = Y[0], ovflow = 0; // will keep a running sum of all new elements
515 tempP = 0; // will keep a partial sum of all old elements
516 for (i=1; (i<N); i++)
517 {
518 myuint_t tempPO = MULWU(tempP);
519 tempP = modadd(tempP, Y[i]);
520 tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); // new Y[i] = old Y[i] + old partial * m
521 Y[i] = tempV;
522 sumtot += tempV; if (sumtot < tempV) {ovflow++;}
523 }
524 return MIXMAX_MOD_MERSENNE(MIXMAX_MOD_MERSENNE(sumtot) + (ovflow <<3 ));
525 }
526
get_next()527 myuint_t MixMaxRng::get_next()
528 {
529 int i;
530 i=S.counter;
531
532 if ((i<=(N-1)) )
533 {
534 S.counter++;
535 return S.V[i];
536 }
537 else
538 {
539 S.sumtot = iterate_raw_vec(S.V.data(), S.sumtot);
540 S.counter=2;
541 return S.V[1];
542 }
543 }
544
precalc()545 myuint_t MixMaxRng::precalc()
546 {
547 int i;
548 myuint_t temp;
549 temp = 0;
550 for (i=0; i < N; i++){
551 temp = MIXMAX_MOD_MERSENNE(temp + S.V[i]);
552 }
553 S.sumtot = temp;
554 return temp;
555 }
556
get_next_float_packbits()557 double MixMaxRng::get_next_float_packbits()
558 {
559 myuint_t Z=get_next();
560 return convert1double(Z);
561 }
562
seed_vielbein(unsigned int index)563 void MixMaxRng::seed_vielbein(unsigned int index)
564 {
565 int i;
566 if (index<N)
567 {
568 for (i=0; i < N; i++){
569 S.V[i] = 0;
570 }
571 S.V[index] = 1;
572 }
573 else
574 {
575 std::terminate();
576 }
577 S.counter = N; // set the counter to N if iteration should happen right away
578 S.sumtot = 1;
579 }
580
581 #define MIXMAX_SEED_WAS_ZERO 0xFF02
582
seed_spbox(myuint_t seed)583 void MixMaxRng::seed_spbox(myuint_t seed)
584 {
585 // a 64-bit LCG from Knuth line 26, in combination with a bit swap is used to seed
586
587 const myuint_t MULT64=6364136223846793005ULL;
588 int i;
589
590 myuint_t sumtot=0,ovflow=0;
591 if (seed == 0)
592 {
593 fprintf(stderr, " try seeding with nonzero seed next time!\n");
594 exit(MIXMAX_SEED_WAS_ZERO);
595 }
596
597 myuint_t l = seed;
598
599 for (i=0; i < N; i++){
600 l*=MULT64; l = (l << 32) ^ (l>>32);
601 S.V[i] = l & M61;
602 sumtot += S.V[(i)]; if (sumtot < S.V[(i)]) {ovflow++;}
603 }
604 S.counter = N; // set the counter to N if iteration should happen right away
605 S.sumtot = MIXMAX_MOD_MERSENNE(MIXMAX_MOD_MERSENNE(sumtot) + (ovflow <<3 ));
606 }
607
608 #undef MIXMAX_SEED_WAS_ZERO
609
seed_uniquestream(myID_t clusterID,myID_t machineID,myID_t runID,myID_t streamID)610 void MixMaxRng::seed_uniquestream( myID_t clusterID, myID_t machineID, myID_t runID, myID_t streamID )
611 {
612 seed_vielbein(0);
613 S.sumtot = apply_bigskip(S.V.data(), S.V.data(), clusterID, machineID, runID, streamID );
614 S.counter = 1;
615 }
616
apply_bigskip(myuint_t * Vout,myuint_t * Vin,myID_t clusterID,myID_t machineID,myID_t runID,myID_t streamID)617 myuint_t MixMaxRng::apply_bigskip( myuint_t* Vout, myuint_t* Vin, myID_t clusterID, myID_t machineID, myID_t runID, myID_t streamID )
618 {
619 /*
620 makes a derived state vector, Vout, from the mother state vector Vin
621 by skipping a large number of steps, determined by the given seeding ID's
622
623 it is mathematically guaranteed that the substreams derived in this way from the SAME (!!!) Vin will not collide provided
624 1) at least one bit of ID is different
625 2) less than 10^100 numbers are drawn from the stream
626 (this is good enough : a single CPU will not exceed this in the lifetime of the universe, 10^19 sec,
627 even if it had a clock cycle of Planch time, 10^44 Hz )
628
629 Caution: never apply this to a derived vector, just choose some mother vector Vin, for example the unit vector by seed_vielbein(X,0),
630 and use it in all your runs, just change runID to get completely nonoverlapping streams of random numbers on a different day.
631
632 clusterID and machineID are provided for the benefit of large organizations who wish to ensure that a simulation
633 which is running in parallel on a large number of clusters and machines will have non-colliding source of random numbers.
634
635 did i repeat it enough times? the non-collision guarantee is absolute, not probabilistic
636
637 */
638
639 const myuint_t skipMat17[128][17] =
640 #include "CLHEP/Random/mixmax_skip_N17.icc"
641 ;
642
643 const myuint_t* skipMat[128];
644 for (int i=0; i<128; i++) { skipMat[i] = skipMat17[i];}
645
646 myID_t IDvec[4] = {streamID, runID, machineID, clusterID};
647 int r,i,j, IDindex;
648 myID_t id;
649 myuint_t Y[N], cum[N];
650 myuint_t coeff;
651 myuint_t* rowPtr;
652 myuint_t sumtot=0;
653
654 for (i=0; i<N; i++) { Y[i] = Vin[i]; sumtot = modadd( sumtot, Vin[i]); } ;
655 for (IDindex=0; IDindex<4; IDindex++)
656 { // go from lower order to higher order ID
657 id=IDvec[IDindex];
658 //printf("now doing ID at level %d, with ID = %d\n", IDindex, id);
659 r = 0;
660 while (id)
661 {
662 if (id & 1)
663 {
664 rowPtr = (myuint_t*)skipMat[r + IDindex*8*sizeof(myID_t)];
665 for (i=0; i<N; i++){ cum[i] = 0; }
666 for (j=0; j<N; j++)
667 { // j is lag, enumerates terms of the poly
668 // for zero lag Y is already given
669 coeff = rowPtr[j]; // same coeff for all i
670 for (i =0; i<N; i++){
671 cum[i] = fmodmulM61( cum[i], coeff , Y[i] ) ;
672 }
673 sumtot = iterate_raw_vec(Y, sumtot);
674 }
675 sumtot=0;
676 for (i=0; i<N; i++){ Y[i] = cum[i]; sumtot = modadd( sumtot, cum[i]); } ;
677 }
678 id = (id >> 1); r++; // bring up the r-th bit in the ID
679 }
680 }
681 sumtot=0;
682 for (i=0; i<N; i++){ Vout[i] = Y[i]; sumtot = modadd( sumtot, Y[i]); }
683 // returns sumtot, and copy the vector over to Vout
684 return (sumtot) ;
685 }
686
687 #if defined(__x86_64__)
mod128(__uint128_t s)688 myuint_t MixMaxRng::mod128(__uint128_t s)
689 {
690 myuint_t s1;
691 s1 = ( ( ((myuint_t)s)&M61 ) + ( ((myuint_t)(s>>64)) * 8 ) + ( ((myuint_t)s) >>BITS) );
692 return MIXMAX_MOD_MERSENNE(s1);
693 }
fmodmulM61(myuint_t cum,myuint_t a,myuint_t b)694 myuint_t MixMaxRng::fmodmulM61(myuint_t cum, myuint_t a, myuint_t b)
695 {
696 __uint128_t temp;
697 temp = (__uint128_t)a*(__uint128_t)b + cum;
698 return mod128(temp);
699 }
700 #else // on all other platforms, including 32-bit linux, PPC and PPC64, ARM and all Windows
fmodmulM61(myuint_t cum,myuint_t s,myuint_t a)701 myuint_t MixMaxRng::fmodmulM61(myuint_t cum, myuint_t s, myuint_t a)
702 {
703 const myuint_t MASK32=0xFFFFFFFFULL;
704 myuint_t o,ph,pl,ah,al;
705 o=(s)*a;
706 ph = ((s)>>32);
707 pl = (s) & MASK32;
708 ah = a>>32;
709 al = a & MASK32;
710 o = (o & M61) + ((ph*ah)<<3) + ((ah*pl+al*ph + ((al*pl)>>32))>>29) ;
711 o += cum;
712 o = (o & M61) + ((o>>61));
713 return o;
714 }
715 #endif
716
modadd(myuint_t foo,myuint_t bar)717 myuint_t MixMaxRng::modadd(myuint_t foo, myuint_t bar)
718 {
719 #if defined(__x86_64__) && defined(__GNUC__) && (!defined(__ICC))
720 //#warning Using assembler routine in modadd
721 myuint_t out;
722 /* Assembler trick suggested by Andrzej Görlich */
723 __asm__ ("addq %2, %0; "
724 "btrq $61, %0; "
725 "adcq $0, %0; "
726 :"=r"(out)
727 :"0"(foo), "r"(bar)
728 );
729 return out;
730 #else
731 return MIXMAX_MOD_MERSENNE(foo+bar);
732 #endif
733 }
734
print_state() const735 void MixMaxRng::print_state() const
736 {
737 int j;
738 std::cout << "mixmax state, file version 1.0\n";
739 std::cout << "N=" << rng_get_N() << "; V[N]={";
740 for (j=0; (j< (rng_get_N()-1) ); j++) {
741 std::cout << S.V[j] << ", ";
742 }
743 std::cout << S.V[rng_get_N()-1];
744 std::cout << "}; ";
745 std::cout << "counter= " << S.counter;
746 std::cout << "sumtot= " << S.sumtot << "\n";
747 }
748
Branch()749 MixMaxRng MixMaxRng::Branch()
750 {
751 S.sumtot = iterate_raw_vec(S.V.data(), S.sumtot); S.counter = 1;
752 MixMaxRng tmp=*this;
753 tmp.BranchInplace(0); // daughter id
754 return tmp;
755 }
756
BranchInplace(int id)757 void MixMaxRng::BranchInplace(int id)
758 {
759 // Dont forget to iterate the mother, when branching the daughter, or else will have collisions!
760 // a 64-bit LCG from Knuth line 26, is used to mangle a vector component
761 constexpr myuint_t MULT64=6364136223846793005ULL;
762 myuint_t tmp=S.V[id];
763 S.V[1] *= MULT64; S.V[id] &= M61;
764 S.sumtot = MIXMAX_MOD_MERSENNE( S.sumtot + S.V[id] - tmp + M61);
765 S.sumtot = iterate_raw_vec(S.V.data(), S.sumtot);// printf("iterating!\n");
766 S.counter = 1;
767 }
768
769 } // namespace CLHEP
770