Geant4-11
RanluxppEngine.cc
Go to the documentation of this file.
1//
2// -*- C++ -*-
3//
4// -----------------------------------------------------------------------
5// HEP Random
6// --- RanluxppEngine ---
7// class implementation file
8// -----------------------------------------------------------------------
9// Implementation of the RANLUX++ generator
10//
11// RANLUX++ is an LCG equivalent of RANLUX using 576 bit numbers.
12//
13// The idea of the generator (such as the initialization method) and the algorithm
14// for the modulo operation are described in
15// A. Sibidanov, *A revision of the subtract-with-borrow random numbergenerators*,
16// *Computer Physics Communications*, 221(2017), 299-303,
17// preprint https://arxiv.org/pdf/1705.03123.pdf
18//
19// The code is loosely based on the Assembly implementation by A. Sibidanov
20// available at https://github.com/sibidanov/ranluxpp/.
21//
22// Compared to the original generator, this implementation contains a fix to ensure
23// that the modulo operation of the LCG always returns the smallest value congruent
24// to the modulus (based on notes by M. Lüscher). Also, the generator converts the
25// LCG state back to RANLUX numbers (implementation based on notes by M. Lüscher).
26// This avoids a bias in the generated numbers because the upper bits of the LCG
27// state, that is smaller than the modulus \f$ m = 2^{576} - 2^{240} + 1 \f$ (not
28// a power of 2!), have a higher probability of being 0 than 1. And finally, this
29// implementation draws 48 random bits for each generated floating point number
30// (instead of 52 bits as in the original generator) to maintain the theoretical
31// properties from understanding the original transition function of RANLUX as a
32// chaotic dynamical system.
33//
34// These modifications and the portable implementation in general are described in
35// J. Hahnfeld, L. Moneta, *A Portable Implementation of RANLUX++*, vCHEP2021
36// preprint https://arxiv.org/pdf/2106.02504.pdf
37
39
42
45
46#include <cassert>
47#include <fstream>
48#include <ios>
49
50namespace CLHEP {
51
52namespace {
53// Number of instances with automatic seed selection.
55
56const uint64_t kA_2048[] = {
57 0xed7faa90747aaad9, 0x4cec2c78af55c101, 0xe64dcb31c48228ec,
58 0x6d8a15a13bee7cb0, 0x20b2ca60cb78c509, 0x256c3d3c662ea36c,
59 0xff74e54107684ed2, 0x492edfcc0cc8e753, 0xb48c187cf5b22097,
60};
61} // namespace
62
64 int numEngines = ++numberOfEngines;
65 setSeed(numEngines);
66}
67
69 theSeed = seed;
70 setSeed(seed);
71}
72
74 get(is);
75}
76
78
79static constexpr int kMaxPos = 9 * 64;
80static constexpr int kBits = 48;
81
83 uint64_t lcg[9];
84 to_lcg(fState, fCarry, lcg);
85 mulmod(kA_2048, lcg);
86 to_ranlux(lcg, fState, fCarry);
87 fPosition = 0;
88}
89
91 if (fPosition + kBits > kMaxPos) {
92 advance();
93 }
94
95 int idx = fPosition / 64;
96 int offset = fPosition % 64;
97 int numBits = 64 - offset;
98
99 uint64_t bits = fState[idx] >> offset;
100 if (numBits < kBits) {
101 bits |= fState[idx + 1] << numBits;
102 }
103 bits &= ((uint64_t(1) << kBits) - 1);
104
105 fPosition += kBits;
106 assert(fPosition <= kMaxPos && "position out of range!");
107
108 return bits;
109}
110
112 // RandomEngine wants a "double random values ranging between ]0,1[", so
113 // exclude all zero bits.
114 uint64_t random;
115 do {
116 random = nextRandomBits();
117 } while (random == 0);
118
119 static constexpr double div = 1.0 / (uint64_t(1) << kBits);
120 return random * div;
121}
122
123void RanluxppEngine::flatArray(const int size, double *vect) {
124 for (int i = 0; i < size; i++) {
125 vect[i] = flat();
126 }
127}
128
129void RanluxppEngine::setSeed(long seed, int) {
130 theSeed = seed;
131
132 uint64_t lcg[9];
133 lcg[0] = 1;
134 for (int i = 1; i < 9; i++) {
135 lcg[i] = 0;
136 }
137
138 uint64_t a_seed[9];
139 // Skip 2 ** 96 states.
140 powermod(kA_2048, a_seed, uint64_t(1) << 48);
141 powermod(a_seed, a_seed, uint64_t(1) << 48);
142 // Skip more states according to seed.
143 powermod(a_seed, a_seed, seed);
144 mulmod(a_seed, lcg);
145
146 to_ranlux(lcg, fState, fCarry);
147 fPosition = 0;
148}
149
150void RanluxppEngine::setSeeds(const long *seeds, int) {
151 theSeeds = seeds;
152 setSeed(*seeds, 0);
153}
154
155void RanluxppEngine::skip(uint64_t n) {
156 int left = (kMaxPos - fPosition) / kBits;
157 assert(left >= 0 && "position was out of range!");
158 if (n < (uint64_t)left) {
159 // Just skip the next few entries in the currently available bits.
160 fPosition += n * kBits;
161 return;
162 }
163
164 n -= left;
165 // Need to advance and possibly skip over blocks.
166 int nPerState = kMaxPos / kBits;
167 int skip = (n / nPerState);
168
169 uint64_t a_skip[9];
170 powermod(kA_2048, a_skip, skip + 1);
171
172 uint64_t lcg[9];
173 to_lcg(fState, fCarry, lcg);
174 mulmod(a_skip, lcg);
175 to_ranlux(lcg, fState, fCarry);
176
177 // Potentially skip numbers in the freshly generated block.
178 int remaining = n - skip * nPerState;
179 assert(remaining >= 0 && "should not end up at a negative position!");
180 fPosition = remaining * kBits;
181 assert(fPosition <= kMaxPos && "position out of range!");
182}
183
184void RanluxppEngine::saveStatus(const char filename[]) const {
185 std::ofstream os(filename);
186 put(os);
187 os.close();
188}
189
190void RanluxppEngine::restoreStatus(const char filename[]) {
191 std::ifstream is(filename);
192 get(is);
193 is.close();
194}
195
197 std::cout
198 << "--------------------- RanluxppEngine status --------------------"
199 << std::endl;
200 std::cout << " fState[] = {";
201 std::cout << std::hex << std::setfill('0');
202 for (int i = 0; i < 9; i++) {
203 if (i % 3 == 0) {
204 std::cout << std::endl << " ";
205 } else {
206 std::cout << " ";
207 }
208 std::cout << "0x" << std::setw(16) << fState[i] << ",";
209 }
210 std::cout << std::endl << " }" << std::endl;
211 std::cout << std::dec;
212 std::cout << " fCarry = " << fCarry << ", fPosition = " << fPosition
213 << std::endl;
214 std::cout
215 << "----------------------------------------------------------------"
216 << std::endl;
217}
218
219std::string RanluxppEngine::name() const { return engineName(); }
220
221std::string RanluxppEngine::engineName() { return "RanluxppEngine"; }
222
223std::string RanluxppEngine::beginTag() { return "RanluxppEngine-begin"; }
224
225std::ostream &RanluxppEngine::put(std::ostream &os) const {
226 os << beginTag() << "\n";
227 const std::vector<unsigned long> state = put();
228 for (unsigned long v : state) {
229 os << v << "\n";
230 }
231 return os;
232}
233
234std::istream &RanluxppEngine::get(std::istream &is) {
235 std::string tag;
236 is >> tag;
237 if (tag != beginTag()) {
238 is.clear(std::ios::badbit | is.rdstate());
239 std::cerr << "No RanluxppEngine found at current position\n";
240 return is;
241 }
242 return getState(is);
243}
244
245std::istream &RanluxppEngine::getState(std::istream &is) {
246 std::vector<unsigned long> state;
247 state.reserve(VECTOR_STATE_SIZE);
248 for (unsigned int i = 0; i < VECTOR_STATE_SIZE; i++) {
249 unsigned long v;
250 is >> v;
251 state.push_back(v);
252 }
253
254 getState(state);
255 return is;
256}
257
258std::vector<unsigned long> RanluxppEngine::put() const {
259 std::vector<unsigned long> v;
260 v.reserve(VECTOR_STATE_SIZE);
261 v.push_back(engineIDulong<RanluxppEngine>());
262
263 // unsigned long is only guaranteed to be 32 bit wide, so chop up the 64 bit
264 // values in fState.
265 for (int i = 0; i < 9; i++) {
266 unsigned long lower = static_cast<uint32_t>(fState[i]);
267 v.push_back(lower);
268 unsigned long upper = static_cast<uint32_t>(fState[i] >> 32);
269 v.push_back(upper);
270 }
271
272 v.push_back(fCarry);
273 v.push_back(fPosition);
274 return v;
275}
276
277bool RanluxppEngine::get(const std::vector<unsigned long> &v) {
278 if (v[0] != engineIDulong<RanluxppEngine>()) {
279 std::cerr << "RanluxppEngine::get(): "
280 << "vector has wrong ID word - state unchanged" << std::endl;
281 return false;
282 }
283 return getState(v);
284}
285
286bool RanluxppEngine::getState(const std::vector<unsigned long> &v) {
287 if (v.size() != VECTOR_STATE_SIZE) {
288 std::cerr << "RanluxppEngine::getState(): "
289 << "vector has wrong length - state unchanged" << std::endl;
290 return false;
291 }
292
293 // Assemble the state vector (see RanluxppEngine::put).
294 for (int i = 0; i < 9; i++) {
295 uint64_t lower = v[2 * i + 1];
296 uint64_t upper = v[2 * i + 2];
297 fState[i] = (upper << 32) + lower;
298 }
299 fCarry = v[19];
300 fPosition = v[20];
301
302 return true;
303}
304
305} // namespace CLHEP
#define CLHEP_ATOMIC_INT_TYPE
Definition: atomic_int.h:14
void showStatus() const override
unsigned fCarry
Carry bit of the RANLUX state.
void saveStatus(const char filename[]="Ranluxpp.conf") const override
std::string name() const override
uint64_t fState[9]
RANLUX state of the generator.
int fPosition
Current position in bits.
void skip(uint64_t n)
std::istream & get(std::istream &is) override
static std::string engineName()
double flat() override
std::vector< unsigned long > put() const override
void setSeeds(const long *seeds, int dummy=0) override
void restoreStatus(const char filename[]="Ranluxpp.conf") override
static const unsigned int VECTOR_STATE_SIZE
void flatArray(const int size, double *vect) override
void setSeed(long seed, int dummy=0) override
std::istream & getState(std::istream &is) override
static std::string beginTag()
static void mulmod(const uint64_t *in1, uint64_t *inout)
Definition: mulmod.h:219
static void powermod(const uint64_t *base, uint64_t *res, uint64_t n)
Definition: mulmod.h:232
Definition: DoubConv.h:17
static constexpr int kMaxPos
static constexpr int kBits
static void to_lcg(const uint64_t *ranlux, unsigned c, uint64_t *lcg)
Definition: ranlux_lcg.h:24
static void to_ranlux(const uint64_t *lcg, uint64_t *ranlux, unsigned &c_out)
Definition: ranlux_lcg.h:55
Definition: xmlparse.cc:187