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