Geant4 Cross Reference |
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