Geant4 Cross Reference |
1 // 1 2 // -*- C++ -*- 3 // 4 // ------------------------------------------- 5 // HEP Random 6 // --- RanluxppEngine -- 7 // class implementation fi 8 // ------------------------------------------- 9 // Implementation of the RANLUX++ generator 10 // 11 // RANLUX++ is an LCG equivalent of RANLUX usi 12 // 13 // The idea of the generator (such as the init 14 // for the modulo operation are described in 15 // A. Sibidanov, *A revision of the subtract-w 16 // *Computer Physics Communications*, 221(2017 17 // preprint https://arxiv.org/pdf/1705.03123.p 18 // 19 // The code is loosely based on the Assembly i 20 // available at https://github.com/sibidanov/r 21 // 22 // Compared to the original generator, this im 23 // that the modulo operation of the LCG always 24 // to the modulus (based on notes by M. Lüsch 25 // LCG state back to RANLUX numbers (implement 26 // This avoids a bias in the generated numbers 27 // state, that is smaller than the modulus \f$ 28 // a power of 2!), have a higher probability o 29 // implementation draws 48 random bits for eac 30 // (instead of 52 bits as in the original gene 31 // properties from understanding the original 32 // chaotic dynamical system. 33 // 34 // These modifications and the portable implem 35 // J. Hahnfeld, L. Moneta, *A Portable Impleme 36 // preprint https://arxiv.org/pdf/2106.02504.p 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 sel 55 CLHEP_ATOMIC_INT_TYPE numberOfEngines(0); 56 57 const uint64_t kA_2048[] = { 58 0xed7faa90747aaad9, 0x4cec2c78af55c101, 0x 59 0x6d8a15a13bee7cb0, 0x20b2ca60cb78c509, 0x 60 0xff74e54107684ed2, 0x492edfcc0cc8e753, 0x 61 }; 62 } // namespace 63 64 RanluxppEngine::RanluxppEngine() : HepRandomEn 65 int numEngines = ++numberOfEngines; 66 setSeed(numEngines); 67 } 68 69 RanluxppEngine::RanluxppEngine(long seed) : He 70 theSeed = seed; 71 setSeed(seed); 72 } 73 74 RanluxppEngine::RanluxppEngine(std::istream &i 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 108 109 return bits; 110 } 111 112 double RanluxppEngine::flat() { 113 // RandomEngine wants a "double random value 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_ 121 return random * div; 122 } 123 124 void RanluxppEngine::flatArray(const int size, 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 *seed 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 ran 159 if (n < (uint64_t)left) { 160 // Just skip the next few entries in the c 161 fPosition += n * kBits; 162 return; 163 } 164 165 n -= left; 166 // Need to advance and possibly skip over bl 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 g 179 int remaining = int(n - skip * nPerState); 180 assert(remaining >= 0 && "should not end up 181 fPosition = remaining * kBits; 182 assert(fPosition <= kMaxPos && "position out 183 } 184 185 void RanluxppEngine::saveStatus(const char fil 186 std::ofstream os(filename); 187 put(os); 188 os.close(); 189 } 190 191 void RanluxppEngine::restoreStatus(const char 192 std::ifstream is(filename); 193 get(is); 194 is.close(); 195 } 196 197 void RanluxppEngine::showStatus() const { 198 std::cout 199 << "--------------------- RanluxppEngine 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) << fSta 210 } 211 std::cout << std::endl << " }" << std::endl; 212 std::cout << std::dec; 213 std::cout << " fCarry = " << fCarry << ", fP 214 << std::endl; 215 std::cout 216 << "------------------------------------ 217 << std::endl; 218 } 219 220 std::string RanluxppEngine::name() const { ret 221 222 std::string RanluxppEngine::engineName() { ret 223 224 std::string RanluxppEngine::beginTag() { retur 225 226 std::ostream &RanluxppEngine::put(std::ostream 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 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 c 241 return is; 242 } 243 return getState(is); 244 } 245 246 std::istream &RanluxppEngine::getState(std::is 247 std::vector<unsigned long> state; 248 state.reserve(VECTOR_STATE_SIZE); 249 for (unsigned int i = 0; i < VECTOR_STATE_SI 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 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 265 // values in fState. 266 for (int i = 0; i < 9; i++) { 267 unsigned long lower = static_cast<uint32_t 268 v.push_back(lower); 269 unsigned long upper = static_cast<uint32_t 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<uns 279 if (v[0] != engineIDulong<RanluxppEngine>()) 280 std::cerr << "RanluxppEngine::get(): " 281 << "vector has wrong ID word - s 282 return false; 283 } 284 return getState(v); 285 } 286 287 bool RanluxppEngine::getState(const std::vecto 288 if (v.size() != VECTOR_STATE_SIZE) { 289 std::cerr << "RanluxppEngine::getState(): 290 << "vector has wrong length - st 291 return false; 292 } 293 294 // Assemble the state vector (see RanluxppEn 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