API reference

This page lists the exported and public package interface. It omits internal helpers.

ImmutableRNGs.ImmutableRNGsModule
ImmutableRNGs

Immutable RNG keys for Julia. A draw is a pure function of its key. Use splitrng or subrng to derive new random work. The contract guarantees tagged address separation between draw families and derivation, not mathematical independence. MutableRNG implements AbstractRNG for code that needs mutable state.

source
ImmutableRNGs.ARS5Type
ARS5(seed::Integer)
ARS5(; key_word0, key_word1, key_word2, key_word3)
ARS5(key::NTuple{4,UInt32})

An immutable ARS-5 key with a 128-bit key and 128-bit counter space. ARS uses the AES round function with a non-cryptographic Weyl key schedule. The package makes no cryptographic or statistical-quality claim beyond its generator checks.

The labeled constructor preserves Random123's native word order: key_word0 is the least-significant 32-bit word, followed by words 1, 2, and 3.

ARS5 requires a host with an AES round instruction. Module initialization checks this capability once through OS and CPU feature queries. The check does not execute an AES intrinsic. The constructor throws when AES is unavailable and provides no software fallback.

Raw and serialized ARS5 values require an AES-capable host. A serialized value can bypass constructors during restoration. Use it only when ImmutableRNGs._has_aes() is true.

source
ImmutableRNGs.AbstractCursorCodecType
AbstractCursorCodec

Supertype for cursor-key codec tokens. Token types select durable key encodings without relying on function or closure identity.

source
ImmutableRNGs.AbstractImmutableRNGType
AbstractImmutableRNG

Root type for immutable RNG keys. It is not a subtype of Random.AbstractRNG. Mutable code may draw twice from one object, which would silently replay an immutable key. Passing a key where an AbstractRNG is required therefore raises MethodError. Use MutableRNG for that interface.

Subtypes are key-only isbits structs. The generator core contract is

bits(rng, counter::UInt64) -> NTuple{N,UInt32}

a pure function suitable for GPU kernels.

source
ImmutableRNGs.IncompatibleCheckpointType
IncompatibleCheckpoint(checkpoint, reason; compatibility_fields...)

An exception raised for an incompatible sequential cursor checkpoint. Optional fields record known compatibility details. An immutable key cannot encode a mutable cursor position, so the legacy-key form leaves these fields as nothing.

source
ImmutableRNGs.MutableRNGType
MutableRNG(key::AbstractImmutableRNG) <: Random.AbstractRNG
MutableRNG(cursor::RNGCursor) <: Random.AbstractRNG

A host-side sequential adapter for immutable RNG keys. The wrapper advances one raw-word cursor. freeze returns that cursor without mutation. Wrappers compare by object identity. Compare frozen cursors to find overlapping replay aliases. The private cache and Reactant slot are not logical cursor state.

source
ImmutableRNGs.Philox4x32Type
Philox4x32(seed::Integer)
Philox4x32(; key_hi, key_lo)
Philox4x32((key_hi, key_lo))

An immutable Philox4x32-10 key with a 64-bit key and internal 128-bit counter space. cuRAND, PyTorch, and TensorFlow use Philox on GPUs. The labeled and tuple raw-key constructors preserve native (key_hi, key_lo) order.

source
ImmutableRNGs.Philox4x32Method
Philox4x32(r::Random.AbstractRNG)
Threefry2x32(r::Random.AbstractRNG)

Seed a key with 64 bits drawn from r. This call advances r. It is equivalent to Philox4x32(rand(r, UInt64)), or the corresponding Threefry2x32 call. If r is a MutableRNG, the draw advances its cursor.

source
ImmutableRNGs.Philox4x32R7Type
Philox4x32R7(seed::Integer)
Philox4x32R7(; key_hi, key_lo)
Philox4x32R7(key::NTuple{2,UInt32})

An immutable Philox4x32-7 key. It uses the Philox4x32 core with 7 rounds instead of 10. R7 uses the published minimum tested round count. It has a smaller safety margin than R10 and performs roughly 30% less work per block. The labeled and tuple raw-key constructors preserve native (key_hi, key_lo) order.

source
ImmutableRNGs.Philox4x32R7Method
Philox4x32R7(r::Random.AbstractRNG)
ARS5(r::Random.AbstractRNG)
Squares64(r::Random.AbstractRNG)

Seed a key with 64 bits drawn from r. This call advances r. If r is a MutableRNG, the draw advances its cursor.

source
ImmutableRNGs.RNGCursorType
RNGCursor(key::AbstractImmutableRNG)
RNGCursor(key::AbstractImmutableRNG; block::Integer, lane::Integer)
RNGCursor(key::AbstractImmutableRNG; position::Integer)

An immutable position in a key's sequential raw-word stream. A normal state identifies a block and lane. The single terminal state is the lane after the final word of the final block.

source
ImmutableRNGs.Squares64Type
Squares64(seed::Integer)
Squares64(; key::Integer)

An experimental Squares64 key from Widynski (2020). Each block is one 64-bit middle-square output after five rounds. The package exports Squares64 for experimentation. It is not a default generator and has no validated external statistical-quality claim.

Squares requires a specific hexadecimal digit structure. Squares64(seed) therefore does not use the seed as its key. It mixes the seed through one Philox4x32 block and repairs the result. The resulting map is deterministic, non-injective, non-surjective, and not externally validated. Pass a raw key as Squares64(key = k). This form rejects keys that do not meet the paper's structure.

source
ImmutableRNGs.StreamExhaustedType
StreamExhausted(requested, block, lane, block_width, maximum_block)

An exception raised when a positive cursor advance passes the final raw word. It records the requested word count, starting block and lane, block width, and maximum valid block.

source
ImmutableRNGs.SupportedDeviceProfileType
SupportedDeviceProfile(; operations)

Immutable descriptor for tested (operation, element_type) pairs. Reserved operations are :cursor_step, :rand_fill, :randn_fill, :randexp_fill, and :range_fill. Hardware, dependency, transform, revision, and measurement metadata belongs in an external acceptance manifest.

source
ImmutableRNGs.Threefry2x32Type
Threefry2x32(seed::Integer)
Threefry2x32(; key_hi, key_lo)
Threefry2x32((key_hi, key_lo))

An immutable Threefry2x32 key with a 64-bit key. JAX uses Threefry by default, which permits bit-level checks against JAX streams. The labeled and tuple raw-key constructors preserve native (key_hi, key_lo) order.

source
ImmutableRNGs.UnsupportedDeviceProfileType
UnsupportedDeviceProfile(; reason)

Result for an unsupported generator and backend pair on a registered backend. reason is a stable symbolic code. Consumers must accept unknown codes.

source
ImmutableRNGs.advance_cursorMethod
advance_cursor(m::MutableRNG, words::Integer) -> m

Check and apply the complete raw-word skip, then clear the private block cache. A poisoned or inflight wrapper raises an error.

source
ImmutableRNGs.advance_cursorMethod
advance_cursor(cursor::RNGCursor, words::Integer) -> RNGCursor

Skip words raw words without drawing and return the new cursor. Zero is an exact no-op. Negative counts raise ArgumentError. Requests beyond capacity raise StreamExhausted.

source
ImmutableRNGs.bitsFunction
bits(rng::AbstractImmutableRNG, counter::UInt64) -> NTuple{N,UInt32}

Return the raw generator block at counter. The method is pure, allocation-free, and kernel safe. blockwords(typeof(rng)) gives the number of words N in each block.

counter addresses a block rather than an element. One block may supply several elements. The mapping depends on the generator. Philox uses the FAMILY_BITS layout. Threefry uses the full raw counter, although family draws reserve its top byte. Squares masks the counter to 56 bits. bits does not define a portable draw-family layout.

source
ImmutableRNGs.cursor_blockFunction
cursor_block(rng, family, block) -> NTuple{W,UInt32}

Return a raw cursor block through the extension interface. W must equal blockwords(typeof(rng)). Words follow canonical increasing-lane order.

source
ImmutableRNGs.cursor_checkpointMethod
cursor_checkpoint(cursor::RNGCursor) -> NamedTuple

Return the canonical ordered, data-only version-one cursor record. It contains the complete key and stream position, so treat it as sensitive and unauthenticated. It provides no authentication, confidentiality, substitution protection, or corruption recovery. Exact ecosystem replay also needs a caller-owned replay manifest. Package, machine, backend, and provenance data belong in that manifest rather than this record.

source
ImmutableRNGs.cursor_checkpoint_codecMethod
cursor_checkpoint_codec(::Type{R}) -> Union{Nothing,Type{<:AbstractCursorCodec}}

Return the durable-key codec token for R. Return nothing when R does not support logical checkpoints. Core cursor support does not imply checkpoint support.

source
ImmutableRNGs.cursor_core_supportedMethod
cursor_core_supported(::Type{R}) -> Bool

Return whether immutable key type R implements the cursor core protocol. The default is false. An extension opts in after defining the required traits.

source
ImmutableRNGs.cursor_device_profileMethod
cursor_device_profile(::Type{R}, ::Type{B}) -> AbstractDeviceProfile

Query the registry without mutation for generator R and concrete backend B. The result distinguishes an unregistered backend, an unsupported pair, and a supported descriptor. Extensions register data instead of defining query methods.

source
ImmutableRNGs.cursor_familiesMethod
cursor_families(::Type{R}) -> NTuple{N,UInt32}

Return the compile-time family tuple for a cursor-capable key type. The empty default admits no family.

source
ImmutableRNGs.cursor_family_idMethod
cursor_family_id(::Val{name}) -> UInt32

Return a published version-one cursor family identifier. The package owns these identifiers. Extensions cannot add methods to this function.

source
ImmutableRNGs.cursor_max_blockMethod
cursor_max_block(::Type{R}) -> UInt64

Return the largest raw-output block address available to an RNGCursor for generator type R. Bundled generators use their draw counter bound. Third-party generators may define a more specific method.

source
ImmutableRNGs.cursor_owner_uuidFunction
cursor_owner_uuid(::Type{R}) -> String

Return the canonical lowercase UUID of the package that owns cursor-capable generator type R. Extensions define this trait without a runtime registry.

source
ImmutableRNGs.cursor_positionMethod
cursor_position(cursor::RNGCursor) -> UInt128

Return the zero-based global raw-word position. The terminal cursor has the total stream capacity as its position.

source
ImmutableRNGs.cursor_remainingMethod
cursor_remaining(cursor::RNGCursor) -> UInt128

Return the number of raw words from cursor through the final word. The terminal cursor has zero remaining words.

source
ImmutableRNGs.decode_cursor_keyFunction
decode_cursor_key(::Type{C}, ::Type{R}, words) -> R

Decode canonical logical key words for generator type R. The method must check the word count, exact integer widths, and generator key invariants.

source
ImmutableRNGs.encode_cursor_keyFunction
encode_cursor_key(::Type{C}, key) -> Tuple

Encode an immutable generator key as the canonical tuple of logical key words for codec token C.

source
ImmutableRNGs.freezeMethod
freeze(m::MutableRNG) -> RNGCursor

Return the current logical cursor without mutation. A poisoned or inflight wrapper has no trusted checkpoint and raises an error.

source
ImmutableRNGs.generateFunction
generate(f, rng, T, dims...; scheduler=:serial) -> Array{T}
generate(f, rng) -> f(subrng(rng, 0))

Build an array of T with f(subrng(rng, i - 1)) at linear index i. The same arguments return the same array within one package version. Assignment converts the return value of f to the explicit element type T.

f receives a bare key rather than a MutableRNG. Each element owns an address-isolated key. f may draw any number of values without changing another element's address. The resulting streams are intended as independent pseudorandom streams. Tagged domains guarantee address separation, not mathematical independence. The function accepts do-block syntax:

generate(rng, Float64, 100) do k
    k1, k2 = splitrng(k)
    rand(k1) < 0.5 ? randn(k2) : 0.0
end

Two draws from the same key address the same element slot in two families. Call splitrng for each draw that needs a separate stream.

The no-dimension form applies f to the key of element 0, so generate(f, rng) == generate(f, rng, T, 1)[1] up to the conversion to T.

CPU and device contract

On an Array, f may wrap its key (f = k -> rand(MutableRNG(k), 1:100)), call packages that expect a Random.AbstractRNG, allocate memory, or use rejection sampling with a data-dependent draw count.

On a device array, such as a CuArray filled by the KernelAbstractions extension, f runs inside a GPU kernel. It must use pure key draws, allocate no memory, create no MutableRNG, and capture no mutable state. Hardware imposes this CPU and device distinction. JAX uses the same boundary.

See also generate!.

Set scheduler=:polyester after loading Polyester to use its @batch scheduler on CPU arrays. The element addresses and results remain unchanged.

source
ImmutableRNGs.generate!Function
generate!(f, rng, A; scheduler=:serial) -> A

Fill A in place. Linear index i receives f(subrng(rng, i - 1)), converted to eltype(A). generate documents the full contract on f, including the CPU and device distinction.

A::Array uses a plain loop by default. Set scheduler=:polyester after loading Polyester for a parallel @batch loop. Both paths use the same addresses and values. Other array types use scheduler=:backend through the KernelAbstractions extension and reject CPU scheduler names.

source
ImmutableRNGs.is_poisonedMethod
is_poisoned(m::MutableRNG) -> Bool

Return whether a dispatched operation left m with an uncertain cursor. Recover it with restore!, copy!, or supported Random.seed!.

source
ImmutableRNGs.mutablyMethod
mutably(f, key_or_cursor) -> (result, next_cursor)

Run scoped mutable work and return its value with the ending RNGCursor. Rethrow exceptions unchanged.

source
ImmutableRNGs.nextrandMethod
nextrand(rng, args...) -> (value, rng′)

Split rng, draw rand(child, args...) from one child, and return the other child as rng′. Scalar draws are pure and allocation-free.

k = Philox4x32(42)
for _ in 1:3
    x, k = nextrand(k)
end

args... passes to pure rand, including ranges, element types, and dimensions. Key continuations use splitting and differ from sequential MutableRNG draws. Use an RNGCursor to match mutable draws.

source
ImmutableRNGs.nextrandMethod
nextrand(cursor::RNGCursor, [T | range], [dims...]) -> (value, next_cursor)

Draw from the strict sequential cursor stream. Unlike the key method, this method walks raw cursor words and matches owned scalar and Array MutableRNG draws. These overloads do not capture generic samplers or distributions.

source
ImmutableRNGs.nextrandexpMethod
nextrandexp(cursor::RNGCursor, [T], [dims...]) -> (value, next_cursor)

Draw an exponential value from the strict sequential cursor stream. Key-based nextrandexp continues to split once per call.

source
ImmutableRNGs.nextrandnMethod
nextrandn(cursor::RNGCursor, [T], [dims...]) -> (value, next_cursor)

Draw a normal value from the strict sequential cursor stream. Key-based nextrandn continues to split once per call.

source
ImmutableRNGs.packed_bitrand!Method
packed_bitrand!(rng::MutableRNG, bits::BitArray) -> bits

Fill bits with one aligned UInt64 cursor slot for each packed storage chunk. Logical BitArray filling instead consumes one raw word for each element and matches scalar Bool draws.

source
ImmutableRNGs.packed_bitrandMethod
packed_bitrand(rng::MutableRNG, dims::Integer...) -> BitArray

Allocate and fill a packed BitArray. Allocation and dimension errors occur before cursor reservation.

source
ImmutableRNGs.rand_native!Function
ImmutableRNGs.rand_native!(rng::AbstractImmutableRNG, A) -> A

Fill A with the device-native RNG path. The GPUArrays extension defines this method for A::AbstractGPUArray. Without the extension, or for a host Array, the call raises MethodError. There is no CPU fallback.

For a fixed device and backend version, rng determines the result. The stream is not portable across devices and is unrelated to the package's pure rand, randn, and randexp streams for the same key. Use Random.rand! through KernelAbstractions for the portable counter-indexed stream.

source
ImmutableRNGs.register_cursor_codec!Method
register_cursor_codec!(::Type{R}, ::Type{C}) -> Type{C}

Register the current checkpoint traits for record-to-type lookup. A lock protects the registry. Repeating the same owner, generator type, codec token, and identifiers is safe. Cursor sampling does not consult this registry. External packages call this function from __init__.

source
ImmutableRNGs.register_cursor_family!Method
register_cursor_family!(owner_uuid, name, id) -> UInt32

Register a literal third-party family ID in the 0x80:0xbf band. Repeating the same normalized UUID, name, and ID is safe. Registration does not enter cursor sampling dispatch.

source
ImmutableRNGs.register_device_profile!Method
register_device_profile!(::Type{R}, ::Type{B}, descriptor)

Register a supported or unsupported profile for generator R and concrete backend B. Repeating an identical registration is safe. A different value for the same pair raises an error. Extensions call this function from __init__.

source
ImmutableRNGs.restore!Method
restore!(m::MutableRNG, key::AbstractImmutableRNG)

Reject a retired key-only checkpoint. A key names a fresh stream and contains no sequential position.

source
ImmutableRNGs.restore!Method
restore!(m::MutableRNG{R}, cursor::RNGCursor{R}) -> m

Replace the wrapper's logical cursor. This operation can recover a poisoned destination but rejects an inflight destination.

source
ImmutableRNGs.restore_cursorMethod
restore_cursor(key::AbstractImmutableRNG)

Reject a retired key-only checkpoint. An immutable key starts a fresh cursor and cannot encode sequential position.

source
ImmutableRNGs.restore_cursorMethod
restore_cursor(record)
restore_cursor(record, ::Type{R})

Validate and restore a canonical cursor record. The explicit-type form skips reverse type lookup but still checks every owner, generator, stream, codec, key-word, and cursor field. Restoration accepts and discards unknown extension metadata. Version 0.1.0 does not execute historical stream laws.

source
ImmutableRNGs.splitrngFunction
splitrng(rng, n=2) -> NTuple{n,typeof(rng)} or Vector

Derive n child keys in a tagged domain outside the draw families. The children are intended as independent pseudorandom streams. The tags guarantee address separation, not mathematical independence. The same rng and call return the same children. Val(n) returns an NTuple, while integer n returns a Vector. This operation corresponds to JAX's split.

source
ImmutableRNGs.stream_idFunction
stream_id(::Type{R}) -> String

Return the current cursor stream-law identifier for generator type R. A stream ID ends in -vN, where N is positive decimal without a leading zero.

source
ImmutableRNGs.subrngMethod
subrng(rng, data::Integer) -> typeof(rng)

Derive a child key from rng and an integer, such as a loop or worker index. The child is intended as an independent pseudorandom stream. The tag separates its address from other domains, but injectivity holds only up to generator collisions. This operation corresponds to JAX's fold_in.

source
ImmutableRNGs.try_advance_cursorMethod
try_advance_cursor(cursor::RNGCursor, words::Integer) -> (next, ok)

Advance without throwing. A negative or exhausted request returns the canonical terminal cursor and false. A valid request returns the advanced cursor and true.

source
ImmutableRNGs.try_mutablyMethod
try_mutably(f, key_or_cursor) -> Union{MutablySuccess,MutablyFailure}

Run f with a temporary mutable wrapper. On success, retain the final cursor. On failure, retain the exception and backtrace. A poisoned or inflight failure returns the caller's checkpoint and marks it uncertain.

source
ImmutableRNGs.try_nextrandMethod
try_nextrand(cursor, [T | range]) -> (value, next_cursor, ok)

Draw a supported scalar primitive or nonempty integer range without throwing on exhaustion. Failure returns a typed sentinel, the terminal cursor, and false without reading a raw block.

source
ImmutableRNGs.try_nextrandexpMethod
try_nextrandexp(cursor, [T]) -> (value, next_cursor, ok)

Draw an exponential value without throwing on exhaustion. Failure returns a quiet NaN, the terminal cursor, and false without reading a raw block.

source
ImmutableRNGs.try_nextrandnMethod
try_nextrandn(cursor, [T]) -> (value, next_cursor, ok)

Draw a normal value without throwing on exhaustion. Failure returns a quiet NaN, the terminal cursor, and false without reading a raw block.

source
ImmutableRNGs.unsafe_cursorMethod
unsafe_cursor(key, block, lane) -> RNGCursor

Construct a cursor without validation. The caller must prove that the state is valid. This function supports total local cursor operations in device work.

source
ImmutableRNGs.unwrapMethod
unwrap(result) -> (value, cursor)

Return a successful result or throw the saved exception at the unwrap call. Julia has no stable public API that attaches an arbitrary saved backtrace to a later throw. MutablyFailure retains the backtrace for diagnostics.

source
ImmutableRNGs.@generateMacro
@generate name = key T dims... body

Call generate with one MutableRNG for each element:

@generate rng = key Float64 100 begin
    x = randn()
    x < 0 ? 0.0 : x
end

expands to

generate(key, Float64, 100) do k
    local rng = MutableRNG(k)
    x = randn(rng)
    x < 0 ? 0.0 : x
end

Inside body, the macro inserts rng into bare rand, randn, and randexp calls and calls qualified by literal Random. It does not modify a call that already passes an immutable key or mutable Random.AbstractRNG. Aliases, other qualified names, and unrelated macro bodies remain unchanged.

Element i receives subrng(key, i - 1), so it retains the purity, addressing, and shape rules of generate. Draws within one element use cursor order. Swapping two draws in body changes their values.

source
ImmutableRNGs.@keyedMacro
@keyed name = key for i in iter
    ...
end

Give each loop iteration an address-isolated MutableRNG bound to name. The macro applies the @generate draw rewrite to the body. Bare rand, randn, and randexp calls receive name as their first argument.

@keyed rng = key for i in 1:n
    out[i] = library_call(rng) + randn()
end

Each iteration starts from subrng(key, i), using the value of the loop variable rather than an execution counter. Scheduling does not change this address. A scheduler macro may wrap the loop:

@keyed rng = key Threads.@threads for i in 1:n
    out[i] = randn()
end

Place the scheduler inside the macro call because macros expand from the outside inward. Threads.@spawn needs no special handling. The wrapper is bound before the spawn, so each task captures its own wrapper. Loading Polyester also permits Polyester.@batch in the same position.

Two rules follow from the index derivation:

  • A plain loop variable must be an Integer at run time. subrng accepts only integers, so another type raises MethodError.
  • A destructuring loop variable uses its first element as the index. In for (i, batch) in enumerate(batches), make i unique.

Repeated iteration values intentionally replay the same substream. Use enumerate(iter) and destructure (i, value) when each occurrence needs a distinct address.

source
ImmutableRNGs.@mutablyMacro
@mutably name = key body

Bind a MutableRNG for key to name, run body, and return (result, cursor′). The returned cursor′ is the frozen RNGCursor.

total, cursor2 = @mutably rng = key sum(rand(100))

The macro applies the @generate draw rewrite to body. Bare rand, randn, and randexp calls receive rng as their first argument. Continue sequential work from cursor′ rather than a key.

source