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