Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DualRand.cc
Go to the documentation of this file.
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
56
57#include <atomic>
58#include <ostream>
59#include <string.h> // for strcmp
60#include <vector>
61#include <iostream>
62
63namespace CLHEP {
64
65namespace {
66 // Number of instances with automatic seed selection
67 CLHEP_ATOMIC_INT_TYPE numberOfEngines(0);
68}
69
70static const int MarkerLen = 64; // Enough room to hold a begin or end marker.
71
72std::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
82 numEngines(numberOfEngines++),
83 tausworthe (1234567 + numEngines + 175321),
84 integerCong(69607 * tausworthe + 54329, numEngines)
85{
86 theSeed = 1234567;
87}
88
91 numEngines(0),
92 tausworthe ((unsigned int)seed + 175321),
93 integerCong(69607 * tausworthe + 54329, 8043) // MF - not numEngines
94{
95 theSeed = seed;
96}
97
98DualRand::DualRand(std::istream & is)
100 numEngines(0)
101{
102 is >> *this;
103}
104
105DualRand::DualRand(int rowIndex, int colIndex)
107 numEngines(0),
108 tausworthe (rowIndex + 1000 * colIndex + 85329),
109 integerCong(69607 * tausworthe + 54329, 1123) // MF - not numengines
110{
111 theSeed = rowIndex;
112}
113
115
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
125void DualRand::flatArray(const int size, double* vect) {
126 for (int i = 0; i < size; ++i) {
127 vect[i] = flat();
128 }
129}
130
131void DualRand::setSeed(long seed, int) {
132 theSeed = seed;
133 tausworthe = Tausworthe((unsigned int)seed + 175321);
134 integerCong = IntegerCong(69607 * tausworthe + 54329, 8043);
135}
136
137void DualRand::setSeeds(const long * seeds, int) {
138 setSeed(seeds ? *seeds : 1234567, 0);
139 theSeeds = seeds;
140}
141
142void 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
153void 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
185 int 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
199DualRand::operator double() {
200 return flat();
201}
202
203DualRand::operator float() {
204 return (float) ( (integerCong ^ tausworthe) * twoToMinus_32()
205 + nearlyTwoToMinus_54() );
206 // add this so that zero never happens
207}
208
209DualRand::operator unsigned int() {
210 return (integerCong ^ tausworthe) & 0xffffffff;
211}
212
213std::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
223std::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
231std::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
248std::string DualRand::beginTag ( ) {
249 return "DualRand-begin";
250}
251
252std::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
288bool 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
302bool 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
315DualRand::Tausworthe::Tausworthe() {
316 words[0] = 1234567;
317 for (wordIndex = 1; wordIndex < 4; ++wordIndex) {
318 words[wordIndex] = 69607 * words[wordIndex-1] + 54329;
319 }
320}
321
322DualRand::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
329DualRand::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
386void DualRand::Tausworthe::put(std::ostream & os) const {
387 char beginMarker[] = "Tausworthe-begin";
388 char endMarker[] = "Tausworthe-end";
389
390 int 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
401void 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
408void 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
437bool
438DualRand::Tausworthe::get(std::vector<unsigned long>::const_iterator & iv){
439 for (int i = 0; i < 4; ++i) {
440 words[i] = *iv++;
441 }
442 wordIndex = *iv++;
443 return true;
444}
445
446DualRand::IntegerCong::IntegerCong()
447: state((unsigned int)3758656018U),
448 multiplier(66565),
449 addend(12341)
450{
451}
452
453DualRand::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
473DualRand::IntegerCong::operator unsigned int() {
474 return state = (state * multiplier + addend) & 0xffffffff;
475}
476
477void DualRand::IntegerCong::put(std::ostream & os) const {
478 char beginMarker[] = "IntegerCong-begin";
479 char endMarker[] = "IntegerCong-end";
480
481 int 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
489void 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
495void 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
521bool
522DualRand::IntegerCong::get(std::vector<unsigned long>::const_iterator & iv) {
523 state = *iv++;
524 multiplier = *iv++;
525 addend = *iv++;
526 return true;
527}
528
529} // namespace CLHEP
#define CLHEP_ATOMIC_INT_TYPE
Definition: atomic_int.h:14
std::string name() const
Definition: DualRand.cc:72
void setSeeds(const long *seeds, int)
Definition: DualRand.cc:137
void showStatus() const
Definition: DualRand.cc:184
std::vector< unsigned long > put() const
Definition: DualRand.cc:223
static const unsigned int VECTOR_STATE_SIZE
Definition: DualRand.h:102
void restoreStatus(const char filename[]="DualRand.conf")
Definition: DualRand.cc:153
virtual ~DualRand()
Definition: DualRand.cc:114
void setSeed(long seed, int)
Definition: DualRand.cc:131
void saveStatus(const char filename[]="DualRand.conf") const
Definition: DualRand.cc:142
void flatArray(const int size, double *vect)
Definition: DualRand.cc:125
virtual std::istream & get(std::istream &is)
Definition: DualRand.cc:231
static std::string engineName()
Definition: DualRand.h:96
double flat()
Definition: DualRand.cc:116
virtual std::istream & getState(std::istream &is)
Definition: DualRand.cc:252
static std::string beginTag()
Definition: DualRand.cc:248
static double twoToMinus_32()
static double twoToMinus_53()
static double nearlyTwoToMinus_54()
static bool checkFile(std::istream &file, const std::string &filename, const std::string &classname, const std::string &methodname)
Definition: RandomEngine.cc:47
Definition: DoubConv.h:17
bool possibleKeywordInput(IS &is, const std::string &key, T &t)
Definition: RandomEngine.h:166