Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/externals/clhep/src/RanluxppEngine.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  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 
 38 #include "CLHEP/Random/RanluxppEngine.h"
 39 
 40 #include "CLHEP/Random/engineIDulong.h"
 41 #include "CLHEP/Utility/atomic_int.h"
 42 
 43 #include "CLHEP/Random/ranluxpp/mulmod.h"
 44 #include "CLHEP/Random/ranluxpp/ranlux_lcg.h"
 45 
 46 #include <cassert>
 47 #include <fstream>
 48 #include <ios>
 49 #include <cstdint>
 50 
 51 namespace CLHEP {
 52 
 53 namespace {
 54 // Number of instances with automatic seed selection.
 55 CLHEP_ATOMIC_INT_TYPE numberOfEngines(0);
 56 
 57 const uint64_t kA_2048[] = {
 58     0xed7faa90747aaad9, 0x4cec2c78af55c101, 0xe64dcb31c48228ec,
 59     0x6d8a15a13bee7cb0, 0x20b2ca60cb78c509, 0x256c3d3c662ea36c,
 60     0xff74e54107684ed2, 0x492edfcc0cc8e753, 0xb48c187cf5b22097,
 61 };
 62 } // namespace
 63 
 64 RanluxppEngine::RanluxppEngine() : HepRandomEngine() {
 65   int numEngines = ++numberOfEngines;
 66   setSeed(numEngines);
 67 }
 68 
 69 RanluxppEngine::RanluxppEngine(long seed) : HepRandomEngine() {
 70   theSeed = seed;
 71   setSeed(seed);
 72 }
 73 
 74 RanluxppEngine::RanluxppEngine(std::istream &is) : HepRandomEngine() {
 75   get(is);
 76 }
 77 
 78 RanluxppEngine::~RanluxppEngine() = default;
 79 
 80 static constexpr int kMaxPos = 9 * 64;
 81 static constexpr int kBits = 48;
 82 
 83 void RanluxppEngine::advance() {
 84   uint64_t lcg[9];
 85   to_lcg(fState, fCarry, lcg);
 86   mulmod(kA_2048, lcg);
 87   to_ranlux(lcg, fState, fCarry);
 88   fPosition = 0;
 89 }
 90 
 91 uint64_t RanluxppEngine::nextRandomBits() {
 92   if (fPosition + kBits > kMaxPos) {
 93     advance();
 94   }
 95 
 96   int idx = fPosition / 64;
 97   int offset = fPosition % 64;
 98   int numBits = 64 - offset;
 99 
100   uint64_t bits = fState[idx] >> offset;
101   if (numBits < kBits) {
102     bits |= fState[idx + 1] << numBits;
103   }
104   bits &= ((uint64_t(1) << kBits) - 1);
105 
106   fPosition += kBits;
107   assert(fPosition <= kMaxPos && "position out of range!");
108 
109   return bits;
110 }
111 
112 double RanluxppEngine::flat() {
113   // RandomEngine wants a "double random values ranging between ]0,1[", so
114   // exclude all zero bits.
115   uint64_t random;
116   do {
117     random = nextRandomBits();
118   } while (random == 0);
119 
120   static constexpr double div = 1.0 / (uint64_t(1) << kBits);
121   return random * div;
122 }
123 
124 void RanluxppEngine::flatArray(const int size, double *vect) {
125   for (int i = 0; i < size; i++) {
126     vect[i] = flat();
127   }
128 }
129 
130 void RanluxppEngine::setSeed(long seed, int) {
131   theSeed = seed;
132 
133   uint64_t lcg[9];
134   lcg[0] = 1;
135   for (int i = 1; i < 9; i++) {
136     lcg[i] = 0;
137   }
138 
139   uint64_t a_seed[9];
140   // Skip 2 ** 96 states.
141   powermod(kA_2048, a_seed, uint64_t(1) << 48);
142   powermod(a_seed, a_seed, uint64_t(1) << 48);
143   // Skip more states according to seed.
144   powermod(a_seed, a_seed, seed);
145   mulmod(a_seed, lcg);
146 
147   to_ranlux(lcg, fState, fCarry);
148   fPosition = 0;
149 }
150 
151 void RanluxppEngine::setSeeds(const long *seeds, int) {
152   theSeeds = seeds;
153   setSeed(*seeds, 0);
154 }
155 
156 void RanluxppEngine::skip(uint64_t n) {
157   int left = (kMaxPos - fPosition) / kBits;
158   assert(left >= 0 && "position was out of range!");
159   if (n < (uint64_t)left) {
160     // Just skip the next few entries in the currently available bits.
161     fPosition += n * kBits;
162     return;
163   }
164 
165   n -= left;
166   // Need to advance and possibly skip over blocks.
167   int nPerState = kMaxPos / kBits;
168   int skip = int(n / nPerState);
169 
170   uint64_t a_skip[9];
171   powermod(kA_2048, a_skip, skip + 1);
172 
173   uint64_t lcg[9];
174   to_lcg(fState, fCarry, lcg);
175   mulmod(a_skip, lcg);
176   to_ranlux(lcg, fState, fCarry);
177 
178   // Potentially skip numbers in the freshly generated block.
179   int remaining = int(n - skip * nPerState);
180   assert(remaining >= 0 && "should not end up at a negative position!");
181   fPosition = remaining * kBits;
182   assert(fPosition <= kMaxPos && "position out of range!");
183 }
184 
185 void RanluxppEngine::saveStatus(const char filename[]) const {
186   std::ofstream os(filename);
187   put(os);
188   os.close();
189 }
190 
191 void RanluxppEngine::restoreStatus(const char filename[]) {
192   std::ifstream is(filename);
193   get(is);
194   is.close();
195 }
196 
197 void RanluxppEngine::showStatus() const {
198   std::cout
199       << "--------------------- RanluxppEngine status --------------------"
200       << std::endl;
201   std::cout << " fState[] = {";
202   std::cout << std::hex << std::setfill('0');
203   for (int i = 0; i < 9; i++) {
204     if (i % 3 == 0) {
205       std::cout << std::endl << "     ";
206     } else {
207       std::cout << " ";
208     }
209     std::cout << "0x" << std::setw(16) << fState[i] << ",";
210   }
211   std::cout << std::endl << " }" << std::endl;
212   std::cout << std::dec;
213   std::cout << " fCarry = " << fCarry << ", fPosition = " << fPosition
214             << std::endl;
215   std::cout
216       << "----------------------------------------------------------------"
217       << std::endl;
218 }
219 
220 std::string RanluxppEngine::name() const { return engineName(); }
221 
222 std::string RanluxppEngine::engineName() { return "RanluxppEngine"; }
223 
224 std::string RanluxppEngine::beginTag() { return "RanluxppEngine-begin"; }
225 
226 std::ostream &RanluxppEngine::put(std::ostream &os) const {
227   os << beginTag() << "\n";
228   const std::vector<unsigned long> state = put();
229   for (unsigned long v : state) {
230     os << v << "\n";
231   }
232   return os;
233 }
234 
235 std::istream &RanluxppEngine::get(std::istream &is) {
236   std::string tag;
237   is >> tag;
238   if (tag != beginTag()) {
239     is.clear(std::ios::badbit | is.rdstate());
240     std::cerr << "No RanluxppEngine found at current position\n";
241     return is;
242   }
243   return getState(is);
244 }
245 
246 std::istream &RanluxppEngine::getState(std::istream &is) {
247   std::vector<unsigned long> state;
248   state.reserve(VECTOR_STATE_SIZE);
249   for (unsigned int i = 0; i < VECTOR_STATE_SIZE; i++) {
250     unsigned long v;
251     is >> v;
252     state.push_back(v);
253   }
254 
255   getState(state);
256   return is;
257 }
258 
259 std::vector<unsigned long> RanluxppEngine::put() const {
260   std::vector<unsigned long> v;
261   v.reserve(VECTOR_STATE_SIZE);
262   v.push_back(engineIDulong<RanluxppEngine>());
263 
264   // unsigned long is only guaranteed to be 32 bit wide, so chop up the 64 bit
265   // values in fState.
266   for (int i = 0; i < 9; i++) {
267     unsigned long lower = static_cast<uint32_t>(fState[i]);
268     v.push_back(lower);
269     unsigned long upper = static_cast<uint32_t>(fState[i] >> 32);
270     v.push_back(upper);
271   }
272 
273   v.push_back(fCarry);
274   v.push_back(fPosition);
275   return v;
276 }
277 
278 bool RanluxppEngine::get(const std::vector<unsigned long> &v) {
279   if (v[0] != engineIDulong<RanluxppEngine>()) {
280     std::cerr << "RanluxppEngine::get(): "
281               << "vector has wrong ID word - state unchanged" << std::endl;
282     return false;
283   }
284   return getState(v);
285 }
286 
287 bool RanluxppEngine::getState(const std::vector<unsigned long> &v) {
288   if (v.size() != VECTOR_STATE_SIZE) {
289     std::cerr << "RanluxppEngine::getState(): "
290               << "vector has wrong length - state unchanged" << std::endl;
291     return false;
292   }
293 
294   // Assemble the state vector (see RanluxppEngine::put).
295   for (int i = 0; i < 9; i++) {
296     uint64_t lower = v[2 * i + 1];
297     uint64_t upper = v[2 * i + 2];
298     fState[i] = (upper << 32) + lower;
299   }
300   fCarry = (unsigned int)v[19];
301   fPosition = (int)v[20];
302 
303   return true;
304 }
305 
306 } // namespace CLHEP
307