Skip to content

Commit

Permalink
implement RandomSeedSequence
Browse files Browse the repository at this point in the history
  • Loading branch information
andiwand committed Nov 14, 2024
1 parent 09939e1 commit 32ae224
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 166 deletions.
204 changes: 50 additions & 154 deletions Examples/Framework/include/ActsExamples/Framework/RandomNumbers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,59 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//
// RandomNumbers.hpp
// ActsExamples
//
// Created by Andreas Salzburger on 17/05/16.
//
//

#pragma once

#include "ActsExamples/Framework/AlgorithmContext.hpp"

#include <cstdint>
#include <random>
#include <type_traits>

#include <boost/container/small_vector.hpp>

namespace ActsExamples {
struct AlgorithmContext;

/// The random number generator used in the framework.
using RandomEngine = std::mt19937; ///< Mersenne Twister

using RandomSeed = std::uint32_t;

class RandomSeedSequence {
public:
using result_type = std::uint_least32_t;
static constexpr std::size_t N = 4;
using container_type = boost::container::small_vector<result_type, N>;

RandomSeedSequence();
explicit RandomSeedSequence(RandomSeed seed);
template <typename InputIterator>
RandomSeedSequence(InputIterator first, InputIterator last)
: m_seed(first, last) {}
template <typename InputRange>
RandomSeedSequence(const InputRange& range)
: m_seed(range.begin(), range.end()) {}
RandomSeedSequence(std::initializer_list<result_type> il)
: m_seed(il.begin(), il.end()) {}

std::size_t size() const;

container_type generate(std::size_t size) const;

template <typename OutputIterator>
void generate(OutputIterator first, OutputIterator last) const {
container_type result = generate(static_cast<std::size_t>(last - first));
std::copy(result.begin(), result.end(), first);
}

template <typename OutputIterator>
void param(OutputIterator first) const {
std::copy(m_seed.begin(), m_seed.end(), first);
}

RandomSeedSequence append(RandomSeed seed) const;

private:
container_type m_seed;
};

/// Provide event and algorithm specific random number generator.s
///
/// This provides local random number generators, allowing for
Expand All @@ -42,11 +73,17 @@ using RandomEngine = std::mt19937; ///< Mersenne Twister
class RandomNumbers {
public:
struct Config {
std::uint64_t seed = 1234567890u; ///< random seed
RandomSeed seed = 1234567890u; ///< random seed
};

explicit RandomNumbers(const Config& cfg);

RandomSeedSequence createEventAlgorithmSeedSequence(
const AlgorithmContext& context) const;

RandomEngine createEventAlgorithmEngine(
const AlgorithmContext& context) const;

/// Spawn an algorithm-local random number generator. To avoid inefficiencies
/// and multiple uses of a given RNG seed, this should only be done once per
/// Algorithm invocation, after what the generator object should be reused.
Expand All @@ -60,151 +97,10 @@ class RandomNumbers {
///
/// This should only be used in special cases e.g. where a custom
/// random engine is used and `spawnGenerator` can not be used.
std::uint64_t generateSeed(const AlgorithmContext& context) const;
RandomSeed generateSeed(const AlgorithmContext& context) const;

private:
Config m_cfg;
};

/// Distribute entropy from a sequence of input values to a sequence of output
/// values.
///
/// This function is copied from std::seed_seq::generate() and modified to avoid
/// the intermediate std::vector.
///
/// @tparam InputIteratorBegin is the type of the input iterator begin
/// @tparam InputIteratorEnd is the type of the input iterator end
/// @tparam OutputIteratorBegin is the type of the output iterator begin
/// @tparam OutputIteratorEnd is the type of the output iterator end
///
/// @param inputIteratorBegin is the begin of the input sequence
/// @param inputIteratorEnd is the end of the input sequence
/// @param outputIteratorBegin is the begin of the output sequence
/// @param outputIteratorEnd is the end of the output sequence
template <typename InputIteratorBegin, typename InputIteratorEnd,
typename OutputIteratorBegin, typename OutputIteratorEnd>
void distributeEntropy(InputIteratorBegin inputIteratorBegin,
InputIteratorEnd inputIteratorEnd,
OutputIteratorBegin outputIteratorBegin,
OutputIteratorEnd outputIteratorEnd)
requires((std::input_iterator<InputIteratorBegin> &&
std::random_access_iterator<InputIteratorBegin> &&
std::is_assignable_v<std::uint_least32_t,
decltype(*inputIteratorBegin)>) &&
(std::input_iterator<InputIteratorEnd> &&
std::random_access_iterator<InputIteratorEnd> &&
std::is_assignable_v<std::uint_least32_t,
decltype(*inputIteratorEnd)>) &&
(std::output_iterator<OutputIteratorBegin, std::uint_least32_t> &&
std::random_access_iterator<OutputIteratorBegin>) &&
(std::output_iterator<OutputIteratorEnd, std::uint_least32_t> &&
std::random_access_iterator<OutputIteratorEnd>))
{
using result_type = std::uint_least32_t;

if (inputIteratorBegin == inputIteratorEnd) {
return;
}
if (outputIteratorBegin == outputIteratorEnd) {
throw std::invalid_argument("outputIteratorBegin == outputIteratorEnd");
}

const auto __v_ = inputIteratorBegin;
const auto __v_size =
static_cast<std::size_t>(inputIteratorEnd - inputIteratorBegin);
const auto __first = outputIteratorBegin;
const auto __last = outputIteratorEnd;

std::fill(__first, __last, 0x8b8b8b8b);
const std::size_t __n = static_cast<std::size_t>(__last - __first);
const std::size_t __s = __v_size;
const std::size_t __t = (__n >= 623) ? 11
: (__n >= 68) ? 7
: (__n >= 39) ? 5
: (__n >= 7) ? 3
: (__n - 1) / 2;
const std::size_t __p = (__n - __t) / 2;
const std::size_t __q = __p + __t;
const std::size_t __m = std::max(__s + 1, __n);
// __k = 0;
{
result_type __r =
1664525 * _Tp(__first[0] ^ __first[__p] ^ __first[__n - 1]);
__first[__p] += __r;
__r += __s;
__first[__q] += __r;
__first[0] = __r;
}
// Initialize indexing terms used with if statements as an optimization to
// avoid calculating modulo n on every loop iteration for each term.
std::size_t __kmodn = 0; // __k % __n
std::size_t __k1modn = __n - 1; // (__k - 1) % __n
std::size_t __kpmodn = __p % __n; // (__k + __p) % __n
std::size_t __kqmodn = __q % __n; // (__k + __q) % __n

for (std::size_t __k = 1; __k <= __s; ++__k) {
if (++__kmodn == __n) {
__kmodn = 0;
}
if (++__k1modn == __n) {
__k1modn = 0;
}
if (++__kpmodn == __n) {
__kpmodn = 0;
}
if (++__kqmodn == __n) {
__kqmodn = 0;
}

result_type __r =
1664525 * _Tp(__first[__kmodn] ^ __first[__kpmodn] ^ __first[__k1modn]);
__first[__kpmodn] += __r;
__r += __kmodn + __v_[__k - 1];
__first[__kqmodn] += __r;
__first[__kmodn] = __r;
}
for (std::size_t __k = __s + 1; __k < __m; ++__k) {
if (++__kmodn == __n) {
__kmodn = 0;
}
if (++__k1modn == __n) {
__k1modn = 0;
}
if (++__kpmodn == __n) {
__kpmodn = 0;
}
if (++__kqmodn == __n) {
__kqmodn = 0;
}

result_type __r =
1664525 * _Tp(__first[__kmodn] ^ __first[__kpmodn] ^ __first[__k1modn]);
__first[__kpmodn] += __r;
__r += __kmodn;
__first[__kqmodn] += __r;
__first[__kmodn] = __r;
}
for (std::size_t __k = __m; __k < __m + __n; ++__k) {
if (++__kmodn == __n) {
__kmodn = 0;
}
if (++__k1modn == __n) {
__k1modn = 0;
}
if (++__kpmodn == __n) {
__kpmodn = 0;
}
if (++__kqmodn == __n) {
__kqmodn = 0;
}

result_type __r = 1566083941 * _Tp(__first[__kmodn] + __first[__kpmodn] +
__first[__k1modn]);
__first[__kpmodn] ^= __r;
__r -= __kmodn;
__first[__kqmodn] ^= __r;
__first[__kmodn] = __r;
}
}

} // namespace ActsExamples
Loading

0 comments on commit 32ae224

Please sign in to comment.