File indexing completed on 2026-10-26 09:34:38
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #pragma once
0014
0015 #include "corecel/Assert.hh"
0016 #include "corecel/random/data/RanluxppRngData.hh"
0017 #include "corecel/random/distribution/GenerateCanonical.hh"
0018 #include "corecel/random/distribution/detail/GenerateCanonical32.hh"
0019 #include "corecel/sys/ThreadId.hh"
0020
0021 #include "detail/RanluxppImpl.hh"
0022
0023 namespace celeritas
0024 {
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055 class RanluxppRngEngine
0056 {
0057 public:
0058
0059
0060 using result_type = std::uint32_t;
0061 using Initializer_t = RanluxppInitializer;
0062 using ParamsRef = NativeCRef<RanluxppRngParamsData>;
0063 using StateRef = NativeRef<RanluxppRngStateData>;
0064 using RngStateInitializer_t = RanluxppRngStateInitializer;
0065
0066
0067 public:
0068
0069 CELER_FUNCTION RanluxppRngEngine(
0070 ParamsRef const& params, StateRef const& state, TrackSlotId tid)
0071 : params_(params)
0072 {
0073 CELER_EXPECT(tid < state.state.size());
0074 state_ = &state.state[tid];
0075 }
0076
0077
0078 static CELER_CONSTEXPR_FUNCTION result_type min() { return 0u; }
0079
0080
0081 static CELER_CONSTEXPR_FUNCTION result_type max()
0082 {
0083 return celeritas::numeric_limits<result_type>::max();
0084 }
0085
0086
0087 inline CELER_FUNCTION RanluxppRngEngine& operator=(
0088 Initializer_t const& init);
0089
0090
0091 inline CELER_FUNCTION RanluxppRngEngine& operator=(
0092 RngStateInitializer_t const& state_init);
0093
0094
0095 inline CELER_FUNCTION result_type operator()();
0096
0097
0098 inline CELER_FUNCTION void discard(RanluxppUInt count);
0099
0100
0101 inline CELER_FUNCTION RngStateInitializer_t branch();
0102
0103
0104 inline CELER_FUNCTION void advance() { this->advance(*state_); }
0105
0106 private:
0107
0108
0109
0110 inline CELER_FUNCTION void advance(RanluxppRngState& state);
0111
0112
0113 static constexpr int offset_ = 48;
0114 ParamsRef const& params_;
0115 RanluxppRngState* state_;
0116 };
0117
0118
0119
0120
0121
0122 template<class RealType>
0123 struct GenerateCanonical<RanluxppRngEngine, RealType>
0124 {
0125
0126
0127 using real_type = RealType;
0128 using result_type = RealType;
0129
0130
0131
0132 static constexpr auto policy = GenerateCanonicalPolicy::builtin32;
0133
0134
0135 CELER_FORCEINLINE_FUNCTION result_type operator()(RanluxppRngEngine& rng)
0136 {
0137 return detail::GenerateCanonical32<RealType>()(rng);
0138 }
0139 };
0140
0141
0142
0143
0144
0145
0146
0147 inline CELER_FUNCTION RanluxppRngEngine& RanluxppRngEngine::operator=(
0148 Initializer_t const& init)
0149 {
0150
0151 RanluxppArray9 new_a_seed = celeritas::detail::compute_power_modulus(
0152 params_.advance_sequence, init.seed + init.subsequence);
0153
0154
0155 state_->value = celeritas::detail::to_ranlux(new_a_seed);
0156 state_->position = 0;
0157
0158
0159 if (init.offset > 0)
0160 {
0161 this->discard(init.offset);
0162 }
0163 return *this;
0164 }
0165
0166
0167
0168
0169
0170 inline CELER_FUNCTION RanluxppRngEngine& RanluxppRngEngine::operator=(
0171 RngStateInitializer_t const& state_init)
0172 {
0173 (*state_).value = state_init.value;
0174 return *this;
0175 }
0176
0177
0178
0179
0180
0181 CELER_FUNCTION void RanluxppRngEngine::discard(RanluxppUInt n)
0182 {
0183 CELER_EXPECT(n > 0);
0184 CELER_ASSERT(params_.max_position > 0);
0185
0186 int left = (params_.max_position - state_->position) / offset_;
0187 CELER_ASSERT(left >= 0);
0188 if (n < static_cast<RanluxppUInt>(left))
0189 {
0190
0191
0192 state_->position += n * offset_;
0193 CELER_ASSERT(state_->position <= params_.max_position);
0194 return;
0195 }
0196
0197 n -= left;
0198
0199
0200 constexpr int n_per_state = ParamsRef::max_position / offset_;
0201 int skip = n / n_per_state;
0202
0203 RanluxppArray9 a_skip = celeritas::detail::compute_power_modulus(
0204 params_.advance_state, skip + 1);
0205
0206 RanluxppArray9 lcg = celeritas::detail::to_lcg(state_->value);
0207 lcg = celeritas::detail::compute_mod_multiply(a_skip, lcg);
0208 state_->value = celeritas::detail::to_ranlux(lcg);
0209
0210
0211 int remaining = n - skip * n_per_state;
0212 CELER_ASSERT(remaining >= 0);
0213 state_->position = remaining * offset_;
0214 CELER_ENSURE(state_->position <= params_.max_position);
0215 }
0216
0217
0218
0219
0220
0221 CELER_FUNCTION auto RanluxppRngEngine::operator()() -> result_type
0222 {
0223 if (state_->position + offset_ > params_.max_position)
0224 {
0225 this->advance(*state_);
0226 }
0227
0228
0229 int idx = state_->position / 64;
0230 int offset = state_->position % 64;
0231 int num_bits = 64 - offset;
0232
0233 RanluxppUInt bits = state_->value.number[idx] >> offset;
0234 if (num_bits < offset_)
0235 {
0236 bits |= state_->value.number[idx + 1] << num_bits;
0237 }
0238 bits &= ((RanluxppUInt(1) << offset_) - 1);
0239
0240 state_->position += offset_;
0241
0242 CELER_ENSURE(state_->position <= params_.max_position);
0243 return bits & 0xffffffffu;
0244 }
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259 CELER_FUNCTION RanluxppRngEngine::RngStateInitializer_t
0260 RanluxppRngEngine::branch()
0261 {
0262
0263 RanluxppRngState new_state = *state_;
0264
0265
0266 this->advance(*state_);
0267
0268
0269 for (auto i : celeritas::range(9))
0270 {
0271 new_state.value.number[i] ^= (*state_).value.number[i];
0272 }
0273
0274
0275 this->advance(new_state);
0276
0277
0278 RngStateInitializer_t initializer{new_state.value};
0279
0280 return initializer;
0281 }
0282
0283
0284
0285
0286
0287 CELER_FUNCTION void RanluxppRngEngine::advance(RanluxppRngState& state)
0288 {
0289 RanluxppArray9 lcg = celeritas::detail::to_lcg(state.value);
0290 lcg = celeritas::detail::compute_mod_multiply(params_.advance_state, lcg);
0291 state.value = celeritas::detail::to_ranlux(lcg);
0292 state.position = 0;
0293 }
0294
0295
0296 }