Keys, cursors, and wrappers
The package has three state models for three common ownership patterns. Choose the state model before writing the stochastic code.
Immutable keys
Use a key when work has stable names or indices. The same operation on the same key returns the same result.
using ImmutableRNGs, Random
key = Philox4x32(42)
first_draw = rand(key)
second_draw = rand(key)
first_draw == second_drawtrueDerive a new key for new random work. splitrng creates a fixed group. subrng maps an application address to one key.
left, right = splitrng(key)
trial_17 = subrng(left, 17)
(left != right, rand(trial_17) == rand(subrng(left, 17)))(true, true)Sequential cursors
Use RNGCursor when call order defines one sequence. Each call returns a value and the next cursor, so state changes remain explicit.
cursor = RNGCursor(right)
x, cursor = nextrand(cursor)
z, cursor = nextrandn(cursor)
(x, z, cursor_position(cursor))(0.18103236415138924, -1.5750850148063078, 0x00000000000000000000000000000004)Copying a cursor replays its suffix. Pass the returned cursor forward unless replay is intentional.
Mutable wrappers
Use MutableRNG for code that requires Random.AbstractRNG. The wrapper owns a cursor. freeze returns the cursor and omits the private cache.
rng = MutableRNG(RNGCursor(right))
x_mutable = rand(rng)
z_mutable = randn(rng)
checkpoint = freeze(rng)
(x_mutable == x, z_mutable == z, checkpoint == cursor)(true, true, true)The wrapper adapts a cursor to mutable code. Give each sequential chain its own derived key and wrapper.
See the complete 01_basics.jl and 12_step_verbs.jl examples.