MooseRandomPerturbation

Overview

MooseRandomPerturbation generates a keyed pseudo-random permutation of the integers [0, n) using a balanced Feistel network. Given the same seed and n, the mapping is fully deterministic and bijective: every input in [0, n) maps to a unique output in [0, n), and different seeds produce statistically independent permutations. This makes it useful for shuffling a fixed-size index set (e.g., sample or row indices) reproducibly without materializing the full permutation in memory.

 * Generates a keyed pseudo-random permutation of the integers [0, n) using a
 * balanced Feistel network. Given the same seed and n, the mapping is fully
 * deterministic and bijective: every input in [0, n) maps to a unique output
 * in [0, n). Different seeds produce statistically independent permutations.
 *
 * Because n need not be a power of two, the Feistel network operates on a
 * padded domain of size 2^(2*half_bits) >= n and uses cycle-walking: if the
 * raw output falls outside [0, n) it is re-applied until a valid index is
 * reached. This guarantees termination because the padded permutation is
 * itself a bijection and n > 0 ensures at least one valid output exists.
 *
 * The permutation is also invertible via invert(), which runs the Feistel
 * rounds in reverse order.
 */
(framework/include/utils/MooseRandomPerturbation.h)

An instance is constructed with a seed, the domain size n, and the number of Feistel rounds to apply (more rounds improve mixing at the cost of throughput):

  MooseRandomPerturbation(uint64_t seed, unsigned int n, unsigned int rounds = 8);

The permute and invert methods then map an index to its permuted value and back, such that invert(permute(x)) == x for every x in [0, n):

  uint32_t permute(uint32_t x) const;
  uint32_t invert(uint32_t y) const;

For example, LatinHypercube constructs one MooseRandomPerturbation per column to shuffle that column's row assignment:

    _shufflers.push_back(std::make_unique<MooseRandomPerturbation>(seed, getNumberOfRows()));
(modules/stochastic_tools/src/samplers/LatinHypercubeSampler.C)

Recovering from a checkpoint

Because the permutation is fully determined by the seed, n, and round count, dataStore/dataLoad specializations serialize only those three values rather than any per-index mapping, allowing a MooseRandomPerturbation (typically held via std::unique_ptr) to be exactly reconstructed on recover:

dataStore(std::ostream & stream, MooseRandomPerturbation & v, void * context)
{
  uint64_t seed = (static_cast<uint64_t>(v._k1) << 32) | v._k0;
  dataStore(stream, seed, context);
  dataStore(stream, v._n, context);
  dataStore(stream, v._rounds, context);
}

template <>
inline void
dataStore(std::ostream & stream, std::unique_ptr<MooseRandomPerturbation> & v, void * context)
{
  dataStore(stream, *v, context);
}

template <>
inline void
dataLoad(std::istream & stream, std::unique_ptr<MooseRandomPerturbation> & v, void * context)
{
  uint64_t seed;
  unsigned int n, rounds;
  dataLoad(stream, seed, context);
  dataLoad(stream, n, context);
  dataLoad(stream, rounds, context);
  v = std::make_unique<MooseRandomPerturbation>(seed, n, rounds);
(framework/include/utils/MooseRandomPerturbation.h)