Geant4 10.7.0
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
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