Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/externals/clhep/src/DualRand.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 // -*- C++ -*-
  2 //
  3 // -----------------------------------------------------------------------
  4 //                           Hep Random
  5 //                        --- DualRand ---
  6 //                   class implementation file
  7 // -----------------------------------------------------------------------
  8 //  Exclusive or of a feedback shift register and integer congruence
  9 //  random number generator.  The feedback shift register uses offsets
 10 //  127 and 97.  The integer congruence generator uses a different
 11 //  multiplier for each stream.  The multipliers are chosen to give
 12 //  full period and maximum "potency" for modulo 2^32.  The period of
 13 //  the combined random number generator is 2^159 - 2^32, and the
 14 //  sequences are different for each stream (not just started in a
 15 //  different place).
 16 //
 17 //  In the original generator used on ACPMAPS:
 18 //  The feedback shift register generates 24 bits at a time, and
 19 //  the high order 24 bits of the integer congruence generator are
 20 //  used.
 21 //
 22 //  Instead, to conform with more modern engine concepts, we generate
 23 //  32 bits at a time and use the full 32 bits of the congruence
 24 //  generator.
 25 //
 26 //  References:
 27 //  Knuth
 28 //  Tausworthe
 29 //  Golomb
 30 //=========================================================================
 31 // Ken Smith      - Removed pow() from flat() method:           21 Jul 1998
 32 //                - Added conversion operators:                  6 Aug 1998
 33 // J. Marraffino  - Added some explicit casts to deal with
 34 //                  machines where sizeof(int) != sizeof(long)  22 Aug 1998
 35 // M. Fischler    - Modified constructors taking seeds to not
 36 //        depend on numEngines (same seeds should 
 37 //        produce same sequences).  Default still 
 38 //        depends on numEngines.      14 Sep 1998
 39 //                - Modified use of the various exponents of 2
 40 //                  to avoid per-instance space overhead and
 41 //                  correct the rounding procedure              15 Sep 1998
 42 // J. Marraffino  - Remove dependence on hepString class        13 May 1999
 43 // M. Fischler    - Put endl at end of a save     10 Apr 2001
 44 // M. Fischler    - In restore, checkFile for file not found    03 Dec 2004
 45 // M. Fischler    - methods for distrib. instacne save/restore  12/8/04    
 46 // M. Fischler    - split get() into tag validation and 
 47 //                  getState() for anonymous restores           12/27/04    
 48 // Mark Fischler  - methods for vector save/restore     3/7/05    
 49 // M. Fischler    - State-saving using only ints, for portability 4/12/05
 50 //
 51 //=========================================================================
 52 
 53 #include "CLHEP/Random/DualRand.h"
 54 #include "CLHEP/Random/engineIDulong.h"
 55 #include "CLHEP/Utility/atomic_int.h"
 56 
 57 #include <atomic>
 58 #include <ostream>
 59 #include <string.h> // for strcmp
 60 #include <vector>
 61 #include <iostream>
 62 
 63 namespace CLHEP {
 64 
 65 namespace {
 66   // Number of instances with automatic seed selection
 67   CLHEP_ATOMIC_INT_TYPE numberOfEngines(0);
 68 }
 69 
 70 static const int MarkerLen = 64; // Enough room to hold a begin or end marker. 
 71 
 72 std::string DualRand::name() const {return "DualRand";}
 73 
 74 // The following constructors (excluding the istream constructor)  fill
 75 // the bits of the tausworthe and the starting state of the integer
 76 // congruence based on the seed.  In addition, it sets up the multiplier
 77 // for the integer congruence based on the stream number, so you have 
 78 // absolutely independent streams.
 79 
 80 DualRand::DualRand() 
 81 : HepRandomEngine(),
 82   numEngines(numberOfEngines++),
 83   tausworthe (1234567 + numEngines + 175321),
 84   integerCong(69607 * tausworthe + 54329, numEngines)
 85 {
 86   theSeed = 1234567;
 87 }
 88 
 89 DualRand::DualRand(long seed)
 90 : HepRandomEngine(),
 91   numEngines(0),
 92   tausworthe ((unsigned int)seed + 175321),
 93   integerCong(69607 * tausworthe + 54329, 8043) // MF - not numEngines
 94 {
 95   theSeed = seed;
 96 }
 97 
 98 DualRand::DualRand(std::istream & is) 
 99 : HepRandomEngine(),
100   numEngines(0)
101 {
102   is >> *this;
103 }
104 
105 DualRand::DualRand(int rowIndex, int colIndex)
106 : HepRandomEngine(),
107   numEngines(0),
108   tausworthe (rowIndex + 1000 * colIndex + 85329),
109   integerCong(69607 * tausworthe + 54329, 1123) // MF - not numengines
110 {
111   theSeed = rowIndex;
112 }
113 
114 DualRand::~DualRand() { }
115 
116 double DualRand::flat() {
117   unsigned int ic ( integerCong );
118   unsigned int t  ( tausworthe  );
119   return ( (t  ^ ic) * twoToMinus_32() +              // most significant part
120            (t >> 11) * twoToMinus_53() +              // fill in remaining bits
121            nearlyTwoToMinus_54()          // make sure non-zero
122          );
123 }
124 
125 void DualRand::flatArray(const int size, double* vect) {
126   for (int i = 0; i < size; ++i) {
127     vect[i] = flat();
128   }
129 }
130 
131 void DualRand::setSeed(long seed, int) {
132   theSeed = seed;
133   tausworthe  = Tausworthe((unsigned int)seed + 175321);
134   integerCong = IntegerCong(69607 * tausworthe + 54329, 8043);
135 }
136 
137 void DualRand::setSeeds(const long * seeds, int) {
138   setSeed(seeds ? *seeds : 1234567, 0);
139   theSeeds = seeds;
140 }
141 
142 void DualRand::saveStatus(const char filename[]) const {
143   std::ofstream outFile(filename, std::ios::out);
144   if (!outFile.bad()) {
145     outFile << "Uvec\n";
146     std::vector<unsigned long> v = put();
147     for (unsigned int i=0; i<v.size(); ++i) {
148       outFile << v[i] << "\n";
149     }
150   }
151 }
152 
153 void DualRand::restoreStatus(const char filename[]) {
154   std::ifstream inFile(filename, std::ios::in);
155   if (!checkFile ( inFile, filename, engineName(), "restoreStatus" )) { 
156     std::cerr << "  -- Engine state remains unchanged\n";
157     return;       
158   }                   
159   if ( possibleKeywordInput ( inFile, "Uvec", theSeed ) ) {
160     std::vector<unsigned long> v;
161     unsigned long xin;
162     for (unsigned int ivec=0; ivec < VECTOR_STATE_SIZE; ++ivec) {
163       inFile >> xin;
164       if (!inFile) {
165         inFile.clear(std::ios::badbit | inFile.rdstate());
166         std::cerr << "\nDualRand state (vector) description improper."
167          << "\nrestoreStatus has failed."
168          << "\nInput stream is probably mispositioned now." << std::endl;
169         return;
170       }
171       v.push_back(xin);
172     }
173     getState(v);
174     return;
175   }
176 
177   if (!inFile.bad()) {
178 //     inFile >> theSeed;  removed -- encompased by possibleKeywordInput
179     tausworthe.get(inFile);
180     integerCong.get(inFile);
181   }
182 }
183 
184 void DualRand::showStatus() const {
185   long pr=std::cout.precision(20);
186   std::cout << std::endl;
187   std::cout <<         "-------- DualRand engine status ---------"
188       << std::endl;
189   std::cout << "Initial seed          = " << theSeed << std::endl;
190   std::cout << "Tausworthe generator  = " << std::endl;
191   tausworthe.put(std::cout);
192   std::cout << "\nIntegerCong generator = " << std::endl;
193   integerCong.put(std::cout);
194   std::cout << std::endl << "-----------------------------------------"
195       << std::endl;
196   std::cout.precision(pr);
197 }
198 
199 DualRand::operator double() {
200    return flat();
201 }
202 
203 DualRand::operator float() {
204   return (float) ( (integerCong ^ tausworthe) * twoToMinus_32() 
205                 + nearlyTwoToMinus_54()    ); 
206           // add this so that zero never happens
207 }
208 
209 DualRand::operator unsigned int() {
210   return (integerCong ^ tausworthe) & 0xffffffff;
211 }
212 
213 std::ostream & DualRand::put(std::ostream & os) const {
214   char beginMarker[] = "DualRand-begin";
215   os << beginMarker << "\nUvec\n";
216   std::vector<unsigned long> v = put();
217   for (unsigned int i=0; i<v.size(); ++i) {
218      os <<  v[i] <<  "\n";
219   }
220   return os;  
221 }
222 
223 std::vector<unsigned long> DualRand::put () const {
224   std::vector<unsigned long> v;
225   v.push_back (engineIDulong<DualRand>());
226   tausworthe.put(v);
227   integerCong.put(v);
228   return v;
229 }
230 
231 std::istream & DualRand::get(std::istream & is) {
232   char beginMarker [MarkerLen];
233   is >> std::ws;
234   is.width(MarkerLen);  // causes the next read to the char* to be <=
235       // that many bytes, INCLUDING A TERMINATION \0 
236       // (Stroustrup, section 21.3.2)
237   is >> beginMarker;
238   if (strcmp(beginMarker,"DualRand-begin")) {
239     is.clear(std::ios::badbit | is.rdstate());
240     std::cerr << "\nInput mispositioned or"
241         << "\nDualRand state description missing or"
242         << "\nwrong engine type found." << std::endl;
243     return is;
244   }
245   return getState(is);
246 }
247 
248 std::string DualRand::beginTag ( )  { 
249   return "DualRand-begin"; 
250 }
251   
252 std::istream & DualRand::getState ( std::istream & is ) {
253   if ( possibleKeywordInput ( is, "Uvec", theSeed ) ) {
254     std::vector<unsigned long> v;
255     unsigned long uu;
256     for (unsigned int ivec=0; ivec < VECTOR_STATE_SIZE; ++ivec) {
257       is >> uu;
258       if (!is) {
259         is.clear(std::ios::badbit | is.rdstate());
260         std::cerr << "\nDualRand state (vector) description improper."
261     << "\ngetState() has failed."
262          << "\nInput stream is probably mispositioned now." << std::endl;
263         return is;
264       }
265       v.push_back(uu);
266     }
267     getState(v);
268     return (is);
269   }
270 
271 //  is >> theSeed;  Removed, encompassed by possibleKeywordInput()
272 
273   char endMarker   [MarkerLen];
274   tausworthe.get(is);
275   integerCong.get(is);
276   is >> std::ws;
277   is.width(MarkerLen);  
278    is >> endMarker;
279   if (strcmp(endMarker,"DualRand-end")) {
280     is.clear(std::ios::badbit | is.rdstate());
281     std::cerr << "DualRand state description incomplete."
282         << "\nInput stream is probably mispositioned now." << std::endl;
283     return is;
284   }
285   return is;
286 }
287 
288 bool DualRand::get(const std::vector<unsigned long> & v) {
289   if ((v[0] & 0xffffffffUL) != engineIDulong<DualRand>()) {
290     std::cerr << 
291       "\nDualRand get:state vector has wrong ID word - state unchanged\n";
292     return false;
293   }
294   if (v.size() != VECTOR_STATE_SIZE) {
295     std::cerr << "\nDualRand get:state vector has wrong size: " 
296     << v.size() << " - state unchanged\n";
297     return false;
298   }
299   return getState(v);
300 }
301 
302 bool DualRand::getState (const std::vector<unsigned long> & v) {
303   std::vector<unsigned long>::const_iterator iv = v.begin()+1;
304   if (!tausworthe.get(iv)) return false;
305   if (!integerCong.get(iv)) return false;
306   if (iv != v.end()) {
307     std::cerr << 
308       "\nDualRand get:state vector has wrong size: " << v.size() 
309   << "\n         Apparently " << iv-v.begin() << " words were consumed\n";
310     return false;
311   }
312   return true;
313 }
314 
315 DualRand::Tausworthe::Tausworthe() {
316   words[0] = 1234567;
317   for (wordIndex = 1; wordIndex < 4; ++wordIndex) {
318     words[wordIndex] = 69607 * words[wordIndex-1] + 54329;
319   } 
320 }
321 
322 DualRand::Tausworthe::Tausworthe(unsigned int seed) {
323   words[0] = seed;
324   for (wordIndex = 1; wordIndex < 4; ++wordIndex) {
325     words[wordIndex] = 69607 * words[wordIndex-1] + 54329;
326   }
327 }
328 
329 DualRand::Tausworthe::operator unsigned int() {
330 
331 // Mathematically:  Consider a sequence of bits b[n].  Repeatedly form
332 // b[0]' = b[127] ^ b[97]; b[n]' = b[n-1].  This sequence will have a very
333 // long period (2**127-1 according to Tausworthe's work).
334 
335 // The actual method used relies on the fact that the operations needed to
336 // form bit 0 for up to 96 iterations never depend on the results of the
337 // previous ones.  So you can actually compute many bits at once.  In fact
338 // you can compute 32 at once -- despite 127 - 97 < 32 -- but 24 was used in
339 // the method used in Canopy, where they wanted only single-precision float
340 // randoms.  I will do 32 here.
341 
342 // When you do it this way, this looks disturbingly like the dread lagged XOR
343 // Fibonacci.  And indeed, it is a lagged Fibonacii, F(4,3, op) with the op
344 // being the XOR of a combination of shifts of the two numbers.  Although
345 // Tausworthe asserted excellent properties, I would be scared to death.
346 // However, the shifting and bit swapping really does randomize this in a
347 // serious way.
348 
349 // Statements have been made to the effect that shift register sequences fail
350 // the parking lot test because they achieve randomness by multiple foldings,
351 // and this produces a characteristic pattern.  We observe that in this
352 // specific algorithm, which has a fairly long lever arm, the foldings become
353 // effectively random.  This is evidenced by the fact that the generator
354 // does pass the Diehard tests, including the parking lot test.
355 
356 // To avoid shuffling of variables in memory, you either have to use circular
357 // pointers (and those give you ifs, which are also costly) or compute at least
358 // a few iterations at once.  We do the latter.  Although there is a possible
359 // trade of room for more speed, by computing and saving 256 instead of 128
360 // bits at once, I will stop at this level of optimization.
361 
362 // To remind:  Each (32-bit) step takes the XOR of bits [127-96] with bits
363 // [95-64] and places it in bits [0-31].  But in the first step, we designate
364 // word0 as bits [0-31], in the second step, word 1 (since the bits it holds
365 // will no longer be needed), then word 2, then word 3.  After this, the
366 // stream contains 128 random bits which we will use as 4 valid 32-bit
367 // random numbers.
368 
369 // Thus at the start of the first step, word[0] contains the newest (used)
370 // 32-bit random, and word[3] the oldest.   After four steps, word[0] again
371 // contains the newest (now unused) random, and word[3] the oldest.
372 // Bit 0 of word[0] is logically the newest bit, and bit 31 of word[3]
373 // the oldest.
374 
375   if (wordIndex <= 0) {
376     for (wordIndex = 0; wordIndex < 4; ++wordIndex) {
377       words[wordIndex] = ( (words[(wordIndex+1) & 3] << 1 ) |
378                                    (words[wordIndex] >> 31)   )
379                        ^ ( (words[(wordIndex+1) & 3] << 31) |
380                                    (words[wordIndex] >>  1)   );
381     }
382   }
383   return words[--wordIndex] & 0xffffffff;
384 }
385 
386 void DualRand::Tausworthe::put(std::ostream & os) const {
387   char beginMarker[] = "Tausworthe-begin";
388   char endMarker[]   = "Tausworthe-end";
389 
390   long pr=os.precision(20);
391   os << " " << beginMarker << " ";
392   for (int i = 0; i < 4; ++i) {
393     os << words[i] << " ";
394   }
395   os << wordIndex;
396   os << " " <<  endMarker  << " ";
397   os << std::endl;
398   os.precision(pr);
399 }
400 
401 void DualRand::Tausworthe::put(std::vector<unsigned long> & v) const {
402   for (int i = 0; i < 4; ++i) {
403     v.push_back(static_cast<unsigned long>(words[i]));
404   }
405   v.push_back(static_cast<unsigned long>(wordIndex)); 
406 }
407 
408 void DualRand::Tausworthe::get(std::istream & is) {
409   char beginMarker [MarkerLen];
410   char endMarker   [MarkerLen];
411 
412   is >> std::ws;
413   is.width(MarkerLen);  // causes the next read to the char* to be <=
414       // that many bytes, INCLUDING A TERMINATION \0 
415       // (Stroustrup, section 21.3.2)
416   is >> beginMarker;
417   if (strcmp(beginMarker,"Tausworthe-begin")) {
418     is.clear(std::ios::badbit | is.rdstate());
419     std::cerr << "\nInput mispositioned or"
420         << "\nTausworthe state description missing or"
421         << "\nwrong engine type found." << std::endl;
422   }
423   for (int i = 0; i < 4; ++i) {
424     is >> words[i];
425   }
426   is >> wordIndex;
427   is >> std::ws;
428   is.width(MarkerLen);  
429   is >> endMarker;
430   if (strcmp(endMarker,"Tausworthe-end")) {
431     is.clear(std::ios::badbit | is.rdstate());
432     std::cerr << "\nTausworthe state description incomplete."
433         << "\nInput stream is probably mispositioned now." << std::endl;
434   }
435 }
436 
437 bool 
438 DualRand::Tausworthe::get(std::vector<unsigned long>::const_iterator & iv){
439   for (int i = 0; i < 4; ++i) {
440     words[i] = (unsigned int)*iv++;
441   }
442   wordIndex = (int)*iv++;
443   return true;
444 }
445 
446 DualRand::IntegerCong::IntegerCong() 
447 : state((unsigned int)3758656018U),
448   multiplier(66565),
449   addend(12341) 
450 {
451 }
452 
453 DualRand::IntegerCong::IntegerCong(unsigned int seed, int streamNumber)
454 : state(seed),
455   multiplier(65536 + 1024 + 5 + (8 * 1017 * streamNumber)),
456   addend(12341)
457 {
458   // As to the multiplier, the following comment was made:
459   // We want our multipliers larger than 2^16, and equal to
460   // 1 mod 4 (for max. period), but not equal to 1 mod 8 
461   // (for max. potency -- the smaller and higher dimension the 
462   // stripes, the better)
463 
464   // All of these will have fairly long periods.  Depending on the choice
465   // of stream number, some of these will be quite decent when considered
466   // as independent random engines, while others will be poor.  Thus these
467   // should not be used as stand-alone engines; but when combined with a
468   // generator known to be good, they allow creation of millions of good
469   // independent streams, without fear of two streams accidentally hitting
470   // nearby places in the good random sequence.
471 }
472 
473 DualRand::IntegerCong::operator unsigned int() {
474   return state = (state * multiplier + addend) & 0xffffffff;
475 }
476 
477 void DualRand::IntegerCong::put(std::ostream & os) const {
478   char beginMarker[] = "IntegerCong-begin";
479   char endMarker[]   = "IntegerCong-end";
480 
481   long pr=os.precision(20);
482   os << " " << beginMarker << " ";
483   os << state << " " << multiplier << " " << addend;
484   os << " " <<  endMarker  << " ";
485   os << std::endl;
486   os.precision(pr);
487 }
488 
489 void DualRand::IntegerCong::put(std::vector<unsigned long> & v) const {
490   v.push_back(static_cast<unsigned long>(state));
491   v.push_back(static_cast<unsigned long>(multiplier));
492   v.push_back(static_cast<unsigned long>(addend));
493 }
494 
495 void DualRand::IntegerCong::get(std::istream & is) {
496   char beginMarker [MarkerLen];
497   char endMarker   [MarkerLen];
498 
499   is >> std::ws;
500   is.width(MarkerLen);  // causes the next read to the char* to be <=
501       // that many bytes, INCLUDING A TERMINATION \0 
502       // (Stroustrup, section 21.3.2)
503   is >> beginMarker;
504   if (strcmp(beginMarker,"IntegerCong-begin")) {
505     is.clear(std::ios::badbit | is.rdstate());
506     std::cerr << "\nInput mispositioned or"
507         << "\nIntegerCong state description missing or"
508         << "\nwrong engine type found." << std::endl;
509   }
510   is >> state >> multiplier >> addend;
511   is >> std::ws;
512   is.width(MarkerLen);  
513   is >> endMarker;
514   if (strcmp(endMarker,"IntegerCong-end")) {
515     is.clear(std::ios::badbit | is.rdstate());
516     std::cerr << "\nIntegerCong state description incomplete."
517         << "\nInput stream is probably mispositioned now." << std::endl;
518   }
519 }
520 
521 bool 
522 DualRand::IntegerCong::get(std::vector<unsigned long>::const_iterator & iv) {
523   state      = (unsigned int)*iv++;
524   multiplier = (unsigned int)*iv++;
525   addend     = (unsigned int)*iv++;
526   return true;
527 }
528 
529 }  // namespace CLHEP
530