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