Geant4 11.2.2
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
MixMaxRng.cc
Go to the documentation of this file.
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-2023
31// July 2023 - Updated class structure upon suggestions from Marco Barbone
32// September 2023 - fix (re-)initialization from Gabriele Cosmo
33// =======================================================================
34
35#include "CLHEP/Random/Random.h"
39
40#include <atomic>
41#include <cmath>
42#include <algorithm>
43#include <iostream>
44#include <string.h> // for strcmp
45#include <vector>
46
47const unsigned long MASK32=0xffffffff;
48
49namespace CLHEP {
50
51namespace {
52 // Number of instances with automatic seed selection
53 CLHEP_ATOMIC_INT_TYPE numberOfEngines(0);
54}
55
56static const int MarkerLen = 64; // Enough room to hold a begin or end marker.
57
60{
61 int numEngines = ++numberOfEngines;
62 setSeed(static_cast<long>(numEngines));
63}
64
67{
68 theSeed=seed;
69 setSeed(seed);
70}
71
72MixMaxRng::MixMaxRng(std::istream& is)
74{
75 get(is);
76}
77
81
83 : HepRandomEngine(rng)
84{
85 std::copy(rng.V, rng.V+N, V);
86 sumtot= rng.sumtot;
87 counter= rng.counter;
88}
89
91{
92 // Check assignment to self
93 //
94 if (this == &rng) { return *this; }
95
96 // Copy base class data
97 //
98 HepRandomEngine::operator=(rng);
99
100 std::copy(rng.V, rng.V+N, V);
101 sumtot= rng.sumtot;
102 counter= rng.counter;
103
104 return *this;
105}
106
107void MixMaxRng::saveStatus( const char filename[] ) const
108{
109 // Create a C file-handle or an object that can accept the same output
110 FILE *fh= fopen(filename, "w");
111 if( fh )
112 {
113 int j;
114 fprintf(fh, "mixmax state, file version 1.0\n" );
115 fprintf(fh, "N=%u; V[N]={", rng_get_N() );
116 for (j=0; (j< (rng_get_N()-1) ); ++j) {
117 fprintf(fh, "%llu, ", (unsigned long long)V[j] );
118 }
119 fprintf(fh, "%llu", (unsigned long long)V[rng_get_N()-1] );
120 fprintf(fh, "}; " );
121 fprintf(fh, "counter=%u; ", counter );
122 fprintf(fh, "sumtot=%llu;\n", (unsigned long long)sumtot );
123 fclose(fh);
124 }
125}
126
127void MixMaxRng::restoreStatus( const char filename[] )
128{
129 // a function for reading the state from a file
130 FILE* fin;
131 if( ( fin = fopen(filename, "r") ) )
132 {
133 char l=0;
134 while ( l != '{' ) { // 0x7B = "{"
135 l=fgetc(fin); // proceed until hitting opening bracket
136 }
137 ungetc(' ', fin);
138 }
139 else
140 {
141 fprintf(stderr, "mixmax -> read_state: error reading file %s\n", filename);
142 throw std::runtime_error("Error in reading state file");
143 }
144
145 myuint_t vecVal;
146 //printf("mixmax -> read_state: starting to read state from file\n");
147 if (!fscanf(fin, "%llu", (unsigned long long*) &V[0]) )
148 {
149 fprintf(stderr, "mixmax -> read_state: error reading file %s\n", filename);
150 throw std::runtime_error("Error in reading state file");
151 }
152
153 for (int i = 1; i < rng_get_N(); ++i)
154 {
155 if (!fscanf(fin, ", %llu", (unsigned long long*) &vecVal) )
156 {
157 fprintf(stderr, "mixmax -> read_state: error reading vector component i=%d from file %s\n", i, filename);
158 throw std::runtime_error("Error in reading state file");
159 }
160 if( vecVal <= MixMaxRng::M61 )
161 {
162 V[i] = vecVal;
163 }
164 else
165 {
166 fprintf(stderr, "mixmax -> read_state: Invalid state vector value= %llu"
167 " ( must be less than %llu ) "
168 " obtained from reading file %s\n"
169 , (unsigned long long)vecVal, (unsigned long long)MixMaxRng::M61, filename);
170 }
171 }
172
173 int incounter;
174 if (!fscanf( fin, "}; counter=%i; ", &incounter))
175 {
176 fprintf(stderr, "mixmax -> read_state: error reading counter from file %s\n", filename);
177 throw std::runtime_error("Error in reading state file");
178 }
179 if( incounter <= rng_get_N() )
180 {
181 counter = incounter;
182 }
183 else
184 {
185 fprintf(stderr, "mixmax -> read_state: Invalid counter = %d"
186 " Must be 0 <= counter < %u\n" , counter, rng_get_N());
187 print_state();
188 throw std::runtime_error("Error in reading state counter");
189 }
190 precalc();
191 myuint_t insumtot;
192 if (!fscanf( fin, "sumtot=%llu\n", (unsigned long long*) &insumtot))
193 {
194 fprintf(stderr, "mixmax -> read_state: error reading checksum from file %s\n", filename);
195 throw std::runtime_error("Error in reading state file");
196 }
197
198 if (sumtot != insumtot)
199 {
200 fprintf(stderr, "mixmax -> checksum error while reading state from file %s - corrupted?\n", filename);
201 throw std::runtime_error("Error in reading state checksum");
202 }
203 fclose(fin);
204}
205
207{
208 std::cout << std::endl;
209 std::cout << "------- MixMaxRng engine status -------" << std::endl;
210
211 std::cout << " Current state vector is:" << std::endl;
212 print_state();
213 std::cout << "---------------------------------------" << std::endl;
214}
215
216// Preferred Seeding method
217// the values of 'Seeds' must be valid 32-bit integers
218// Higher order bits will be ignored!!
219
220void MixMaxRng::setSeeds(const long* Seeds, int seedNum)
221{
222 myID_t seed0, seed1= 0, seed2= 0, seed3= 0;
223
224 if( seedNum < 1 ) { // Assuming at least 2 seeds in vector...
225 seed0= static_cast<myID_t>(Seeds[0]) & MASK32;
226 seed1= static_cast<myID_t>(Seeds[1]) & MASK32;
227 }
228 else
229 {
230 if( seedNum < 4 ) {
231 seed0= static_cast<myID_t>(Seeds[0]) & MASK32;
232 if( seedNum > 1){ seed1= static_cast<myID_t>(Seeds[1]) & MASK32; }
233 if( seedNum > 2){ seed2= static_cast<myID_t>(Seeds[2]) & MASK32; }
234 }
235 if( seedNum >= 4 ) {
236 seed0= static_cast<myID_t>(Seeds[0]) & MASK32;
237 seed1= static_cast<myID_t>(Seeds[1]) & MASK32;
238 seed2= static_cast<myID_t>(Seeds[2]) & MASK32;
239 seed3= static_cast<myID_t>(Seeds[3]) & MASK32;
240 }
241 }
242 theSeed = Seeds[0];
243 theSeeds = Seeds;
244 seed_uniquestream(seed3, seed2, seed1, seed0);
245}
246
248{
249 return "MixMaxRng";
250}
251
252constexpr int MixMaxRng::rng_get_N()
253{
254 return N;
255}
256
257void MixMaxRng::flatArray(const int size, double* vect )
258{
259 for (int i=0; i<size; ++i) { vect[i] = flat(); }
260}
261
262std::ostream & MixMaxRng::put ( std::ostream& os ) const
263{
264 char beginMarker[] = "MixMaxRng-begin";
265 char endMarker[] = "MixMaxRng-end";
266
267 long pr = os.precision(24);
268 os << beginMarker << " ";
269 os << theSeed << "\n";
270 for (int i=0; i<rng_get_N(); ++i) {
271 os << V[i] << "\n";
272 }
273 os << counter << "\n";
274 os << sumtot << "\n";
275 os << endMarker << "\n";
276 os.precision(pr);
277 return os;
278}
279
280std::vector<unsigned long> MixMaxRng::put () const
281{
282 std::vector<unsigned long> vec;
283 vec.push_back (engineIDulong<MixMaxRng>());
284 for (int i=0; i<rng_get_N(); ++i)
285 {
286 vec.push_back(static_cast<unsigned long>(V[i] & MASK32));
287 // little-ended order on all platforms
288 vec.push_back(static_cast<unsigned long>(V[i] >> 32 ));
289 // pack uint64 into a data structure which is 32-bit on some platforms
290 }
291 vec.push_back(static_cast<unsigned long>(counter));
292 vec.push_back(static_cast<unsigned long>(sumtot & MASK32));
293 vec.push_back(static_cast<unsigned long>(sumtot >> 32));
294 return vec;
295}
296
297std::istream & MixMaxRng::get ( std::istream& is)
298{
299 char beginMarker [MarkerLen];
300 is >> std::ws;
301 is.width(MarkerLen); // causes the next read to the char* to be <=
302 // that many bytes, INCLUDING A TERMINATION \0
303 // (Stroustrup, section 21.3.2)
304 is >> beginMarker;
305 if (strcmp(beginMarker,"MixMaxRng-begin")) {
306 is.clear(std::ios::badbit | is.rdstate());
307 std::cerr << "\nInput stream mispositioned or"
308 << "\nMixMaxRng state description missing or"
309 << "\nwrong engine type found." << std::endl;
310 return is;
311 }
312 return getState(is);
313}
314
316{
317 return "MixMaxRng-begin";
318}
319
320std::istream & MixMaxRng::getState ( std::istream& is )
321{
322 char endMarker[MarkerLen];
323 is >> theSeed;
324 for (int i=0; i<rng_get_N(); ++i) is >> V[i];
325 is >> counter;
326 myuint_t checksum;
327 is >> checksum;
328 is >> std::ws;
329 is.width(MarkerLen);
330 is >> endMarker;
331 if (strcmp(endMarker,"MixMaxRng-end")) {
332 is.clear(std::ios::badbit | is.rdstate());
333 std::cerr << "\nMixMaxRng state description incomplete."
334 << "\nInput stream is probably mispositioned now.\n";
335 return is;
336 }
337 if ( counter < 0 || counter > rng_get_N() ) {
338 std::cerr << "\nMixMaxRng::getState(): "
339 << "vector read wrong value of counter from file!"
340 << "\nInput stream is probably mispositioned now.\n";
341 return is;
342 }
343 precalc();
344 if ( checksum != sumtot) {
345 std::cerr << "\nMixMaxRng::getState(): "
346 << "checksum disagrees with value stored in file!"
347 << "\nInput stream is probably mispositioned now.\n";
348 return is;
349 }
350 return is;
351}
352
353bool MixMaxRng::get (const std::vector<unsigned long> & vec)
354{
355 if ((vec[0] & 0xffffffffUL) != engineIDulong<MixMaxRng>())
356 {
357 std::cerr <<
358 "\nMixMaxRng::get(): vector has wrong ID word - state unchanged\n";
359 return false;
360 }
361 return getState(vec);
362}
363
364bool MixMaxRng::getState (const std::vector<unsigned long> & vec)
365{
366 if (vec.size() != VECTOR_STATE_SIZE ) {
367 std::cerr <<
368 "\nMixMaxRng::getState(): vector has wrong length - state unchanged\n";
369 return false;
370 }
371 for (int i=1; i<2*rng_get_N() ; i=i+2) {
372 V[i/2]= ( (vec[i] & MASK32) | ( (myuint_t)(vec[i+1]) << 32 ) );
373 // unpack from a data structure which is 32-bit on some platforms
374 }
375 counter = (int)vec[2*rng_get_N()+1];
376 precalc();
377 if ( ( (vec[2*rng_get_N()+2] & MASK32)
378 | ( (myuint_t)(vec[2*rng_get_N()+3]) << 32 ) ) != sumtot) {
379 std::cerr << "\nMixMaxRng::getState(): vector has wrong checksum!"
380 << "\nInput vector is probably mispositioned now.\n";
381 return false;
382 }
383 return true;
384}
385
386myuint_t MixMaxRng::iterate_raw_vec(myuint_t* Y, myuint_t sumtotOld)
387{
388 // operates with a raw vector, uses known sum of elements of Y
389
390 myuint_t tempP, tempV;
391 Y[0] = ( tempV = sumtotOld);
392 myuint_t insumtot = Y[0], ovflow = 0; // will keep a running sum of all new elements
393 tempP = 0; // will keep a partial sum of all old elements
394 for (int i=1; (i<N); ++i)
395 {
396 myuint_t tempPO = MULWU(tempP);
397 tempP = modadd(tempP, Y[i]);
398 tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); // new Y[i] = old Y[i] + old partial * m
399 Y[i] = tempV;
400 insumtot += tempV; if (insumtot < tempV) {++ovflow;}
401 }
402 return MIXMAX_MOD_MERSENNE(MIXMAX_MOD_MERSENNE(insumtot) + (ovflow <<3 ));
403}
404
405myuint_t MixMaxRng::get_next()
406{
407 int i = counter;
408
409 if ((i<=(N-1)) )
410 {
411 ++counter;
412 return V[i];
413 }
414 else
415 {
416 sumtot = iterate_raw_vec(V, sumtot);
417 counter=2;
418 return V[1];
419 }
420}
421
422myuint_t MixMaxRng::precalc()
423{
424 myuint_t temp = 0;
425 for (int i=0; i < N; ++i) { temp = MIXMAX_MOD_MERSENNE(temp + V[i]); }
426 sumtot = temp;
427 return temp;
428}
429
430void MixMaxRng::state_init()
431{
432 for (int i=1; i < N; ++i) { V[i] = 0; }
433 V[0] = 1;
434 counter = N; // set the counter to N if iteration should happen right away
435 sumtot = 1;
436}
437
438void MixMaxRng::seed_spbox(myuint_t seed)
439{
440 // a 64-bit LCG from Knuth line 26, in combination with a bit swap is used to seed
441
442 if (seed == 0)
443 throw std::runtime_error("try seeding with nonzero seed next time");
444
445 const myuint_t MULT64=6364136223846793005ULL;
446 sumtot = 0;
447
448 myuint_t l = seed;
449
450 for (int i=0; i < N; ++i)
451 {
452 l*=MULT64; l = (l << 32) ^ (l>>32);
453 V[i] = l & M61;
454 sumtot = MIXMAX_MOD_MERSENNE(sumtot + V[(i)]);
455 }
456 counter = N; // set the counter to N if iteration should happen right away
457}
458
459void MixMaxRng::seed_uniquestream( myID_t clusterID, myID_t machineID, myID_t runID, myID_t streamID )
460{
461 state_init();
462 sumtot = apply_bigskip(V, V, clusterID, machineID, runID, streamID );
463 counter = 1;
464}
465
466myuint_t MixMaxRng::apply_bigskip( myuint_t* Vout, myuint_t* Vin, myID_t clusterID, myID_t machineID, myID_t runID, myID_t streamID )
467{
468 /*
469 makes a derived state vector, Vout, from the mother state vector Vin
470 by skipping a large number of steps, determined by the given seeding ID's
471
472 it is mathematically guaranteed that the substreams derived in this way from the SAME (!!!) Vin will not collide provided
473 1) at least one bit of ID is different
474 2) less than 10^100 numbers are drawn from the stream
475 (this is good enough : a single CPU will not exceed this in the lifetime of the universe, 10^19 sec,
476 even if it had a clock cycle of Planch time, 10^44 Hz )
477
478 Caution: never apply this to a derived vector, just choose some mother vector Vin, for example the unit vector by state_init(),
479 and use it in all your runs, just change runID to get completely nonoverlapping streams of random numbers on a different day.
480
481 clusterID and machineID are provided for the benefit of large organizations who wish to ensure that a simulation
482 which is running in parallel on a large number of clusters and machines will have non-colliding source of random numbers.
483
484 did i repeat it enough times? the non-collision guarantee is absolute, not probabilistic
485
486 */
487
488 const myuint_t skipMat17[128][17] =
489 #include "CLHEP/Random/mixmax_skip_N17.icc"
490 ;
491
492 const myuint_t* skipMat[128];
493 for (int i=0; i<128; ++i) { skipMat[i] = skipMat17[i]; }
494
495 myID_t IDvec[4] = {streamID, runID, machineID, clusterID};
496 int r,i,j, IDindex;
497 myID_t id;
498 myuint_t Y[N], cum[N];
500 myuint_t* rowPtr;
501 myuint_t insumtot=0;
502
503 for (i=0; i<N; ++i) { Y[i] = Vin[i]; insumtot = modadd( insumtot, Vin[i]); }
504 for (IDindex=0; IDindex<4; ++IDindex)
505 { // go from lower order to higher order ID
506 id=IDvec[IDindex];
507 //printf("now doing ID at level %d, with ID = %d\n", IDindex, id);
508 r = 0;
509 while (id)
510 {
511 if (id & 1)
512 {
513 rowPtr = (myuint_t*)skipMat[r + IDindex*8*sizeof(myID_t)];
514 for (i=0; i<N; ++i){ cum[i] = 0; }
515 for (j=0; j<N; ++j)
516 { // j is lag, enumerates terms of the poly
517 // for zero lag Y is already given
518 coeff = rowPtr[j]; // same coeff for all i
519 for (i =0; i<N; ++i){
520 cum[i] = fmodmulM61( cum[i], coeff , Y[i] ) ;
521 }
522 insumtot = iterate_raw_vec(Y, insumtot);
523 }
524 insumtot=0;
525 for (i=0; i<N; ++i){ Y[i] = cum[i]; insumtot = modadd( insumtot, cum[i]); } ;
526 }
527 id = (id >> 1); ++r; // bring up the r-th bit in the ID
528 }
529 }
530 insumtot=0;
531 for (i=0; i<N; ++i){ Vout[i] = Y[i]; insumtot = modadd( insumtot, Y[i]); }
532 // returns sumtot, and copy the vector over to Vout
533 return insumtot;
534}
535
536#if defined(__x86_64__)
537 myuint_t MixMaxRng::mod128(__uint128_t s)
538 {
539 myuint_t s1;
540 s1 = ( ( ((myuint_t)s)&M61 ) + ( ((myuint_t)(s>>64)) * 8 ) + ( ((myuint_t)s) >>BITS) );
541 return MIXMAX_MOD_MERSENNE(s1);
542 }
543 myuint_t MixMaxRng::fmodmulM61(myuint_t cum, myuint_t a, myuint_t b)
544 {
545 __uint128_t temp;
546 temp = (__uint128_t)a*(__uint128_t)b + cum;
547 return mod128(temp);
548 }
549#else // on all other platforms, including 32-bit linux, PPC and PPC64, ARM and all Windows
550 myuint_t MixMaxRng::fmodmulM61(myuint_t cum, myuint_t s, myuint_t a)
551 {
552 const myuint_t MASK32=0xFFFFFFFFULL;
553 myuint_t o,ph,pl,ah,al;
554 o=(s)*a;
555 ph = ((s)>>32);
556 pl = (s) & MASK32;
557 ah = a>>32;
558 al = a & MASK32;
559 o = (o & M61) + ((ph*ah)<<3) + ((ah*pl+al*ph + ((al*pl)>>32))>>29) ;
560 o += cum;
561 o = (o & M61) + ((o>>61));
562 return o;
563 }
564#endif
565
566void MixMaxRng::print_state() const
567{
568 std::cout << "mixmax state, file version 1.0\n";
569 std::cout << "N=" << rng_get_N() << "; V[N]={";
570 for (int j=0; (j< (rng_get_N()-1) ); ++j) { std::cout << V[j] << ", "; }
571 std::cout << V[rng_get_N()-1];
572 std::cout << "}; ";
573 std::cout << "counter= " << counter;
574 std::cout << "sumtot= " << sumtot << "\n";
575}
576
577MixMaxRng MixMaxRng::Branch()
578{
579 sumtot = iterate_raw_vec(V, sumtot); counter = 1;
580 MixMaxRng tmp=*this;
581 tmp.BranchInplace(0); // daughter id
582 return tmp;
583}
584
585void MixMaxRng::BranchInplace(int id)
586{
587 // Dont forget to iterate the mother, when branching the daughter, or else will have collisions!
588 // a 64-bit LCG from Knuth line 26, is used to mangle a vector component
589 constexpr myuint_t MULT64=6364136223846793005ULL;
590 myuint_t tmp=V[id];
591 V[1] *= MULT64; V[id] &= M61;
592 sumtot = MIXMAX_MOD_MERSENNE( sumtot + V[id] - tmp + M61);
593 sumtot = iterate_raw_vec(V, sumtot);
594 counter = 1;
595}
596
597} // namespace CLHEP
G4double Y(G4double density)
const unsigned long MASK32
Definition MixMaxRng.cc:47
#define CLHEP_ATOMIC_INT_TYPE
Definition atomic_int.h:14
void restoreStatus(const char filename[]="MixMaxRngState.conf")
Definition MixMaxRng.cc:127
void showStatus() const
Definition MixMaxRng.cc:206
void flatArray(const int size, double *vect)
Definition MixMaxRng.cc:257
static std::string beginTag()
Definition MixMaxRng.cc:315
void saveStatus(const char filename[]="MixMaxRngState.conf") const
Definition MixMaxRng.cc:107
virtual std::istream & get(std::istream &is)
Definition MixMaxRng.cc:297
MixMaxRng & operator=(const MixMaxRng &rng)
Definition MixMaxRng.cc:90
virtual std::istream & getState(std::istream &is)
Definition MixMaxRng.cc:320
std::vector< unsigned long > put() const
Definition MixMaxRng.cc:280
double flat()
Definition MixMaxRng.h:69
void setSeeds(const long *seeds, int seedNum=0)
Definition MixMaxRng.cc:220
void setSeed(long longSeed, int=0)
Definition MixMaxRng.h:81
static std::string engineName()
Definition MixMaxRng.cc:247
#define N
Definition crc32.c:57
std::uint64_t myuint_t
Definition MixMaxRng.h:50
unsigned long engineIDulong()
std::uint32_t myID_t
Definition MixMaxRng.h:49