API reference
Generator and state
TandemRNG.TandemRNG — Module
TandemRNGTandem: an asymmetric duplex random number generator designed for GPUs. Eight 32-bit words, a hidden bijective clock that keys a Philox-shaped multiplicative Feistel on the exposed half, chunked reseeding for splitting and random access, and a bit-aligned stream law with no straddling scalar component draws. The algorithm specification is tandem-spec.
TandemRNG.Tandem8x32 — Type
Tandem8x32{K}Tandem generator with chunks of K steps (a power of two, default 32). Construct from an integer seed, which is whitened through F, or from a raw 128-bit key as four UInt32 words. The second type parameter stores a concrete backend token. New generators use the CPU. Bind with an MLDataDevices device, such as rng |> CUDADevice(), to allocate GPU arrays.
The value is immutable. rand_next(rng, T) returns (x, rng′). rand_fill!(rng, A) fills an array and returns the advanced generator. splitrng, forkrng, and subrng derive child generators. Wrap in Stateful for the Random API.
TandemRNG.Tandem8x32 — Method
Tandem8x32{K}(key::NTuple{4,UInt32}, position = 0)Generator with the raw key at bit position. The position must be below 2^63.
TandemRNG.Tandem8x32 — Method
Tandem8x32{K}(seed::Integer)Generator whose key is seed whitened through the seeding function F. seed must satisfy 0 <= seed < 2^128.
TandemRNG.rngkey — Function
rngkey(rng) -> NTuple{4,UInt32}The raw key.
TandemRNG.rngposition — Function
rngposition(rng) -> UInt64Bits consumed so far, including alignment gaps. Below position 2^63, Tandem8x32{K}(rngkey(rng), rngposition(rng)) restores the stream with CPU binding.
TandemRNG.chunk_length — Function
chunk_length(rng) -> Int
chunk_length(Tandem8x32{K}) -> IntSteps per chunk, the type parameter K.
Draws
TandemRNG.rand_next — Function
rand_next(rng, T) -> (x, rng′)
rand_next(rng, T, dims...) -> (A, rng′)Draw one value of type T and return it with the advanced generator. T is Bool, an 8- to 128-bit integer, Float16, Float32, Float64, a complex of those floats, or Char. Real floats lie in [0, 1); complex values compose two real draws. Char uses the fixed-work Unicode scalar mapping specified in the Tandem8x32 specification. Array draws allocate on the generator's backend. Scalar calls run where they are called: on the host, or inside a device kernel. Binding preserves the key and position.
rand_next(rng, r::AbstractUnitRange{<:Integer}) -> (x, rng′)Draw x uniform on the integers of r by Lemire's method (Appendix A of the specification). The draw width follows the number of values: 32 bits up to 2^32 values, else 64. The element type of r does not change the value, only its type. A rejected draw retries on the next draw of the stream. Ranges of more than 2^64 values are not supported.
TandemRNG.rand_at — Function
rand_at(rng, T, i) -> xThe i-th value (1-based) of rand_fill!(rng, Vector{T}(undef, n)) without advancing rng. Each real component costs one F plus at most K steps of T. Use this for addressed draws; use rand_next or rand_fill! for sequential draws. The complete addressed span must fit without wrapping the stream position.
TandemRNG.rand_fill! — Function
rand_fill!(rng, A::AbstractArray{T}; nthreads = Threads.nthreads()) -> rng′Fill A with values of type T and return the advanced generator. T is one of the draw types of rand_next. The destination backend must match the generator's binding, including for empty arrays. Groups of eight chunks are distributed over nthreads tasks when the fill spans at least 64 KiB of stream output. The result does not depend on the thread count. nthreads is an upper bound; small fills run serially.
On a GPU array (with KernelAbstractions and GPUArraysCore loaded) the fill is queued on the array's device like other array operations and the call returns before it completes. Synchronize the device before reading the array from the host.
rand_fill!(rng, A::AbstractArray, r::AbstractUnitRange{<:Integer}) -> rng′Fill A with integers uniform on r. The draw width follows the number of values as for rand_next, and the fill follows rand_below_fill!: one draw per element, rejected draws retry on their fallback generator, and an empty fill leaves the position unchanged.
TandemRNG.rand_next! — Function
rand_next!(rng, destination; threaded::Bool = false) -> (destination, rng′)Fill destination through rand_fill! and return it with the advanced generator. The returned destination is the supplied object. threaded = true uses up to Threads.nthreads() CPU tasks; the default uses one. Values and draw consumption follow rand_fill!, including when a loaded backend supplies the fill.
TandemRNG.randn_next — Function
randn_next(rng, T = Float64) -> (z, rng′)
randn_next(rng, [T,] dims::Dims; threaded::Bool = false) -> (A, rng′)
randn_next(rng, [T,] n::Integer, dims::Integer...; threaded::Bool = false)Draw native standard normals through normal_next, or allocate and fill an array through randn_next!. T is Float32 or Float64; the default is Float64. Allocating forms use the generator's device binding and the native CPU normal fill. The scalar and fill algorithms retain their respective Appendix A consumption rules.
TandemRNG.randn_next! — Function
randn_next!(rng, destination; threaded::Bool = false) -> (destination, rng′)Fill a Float32 or Float64 CPU destination through normal_fill! and return the same destination with the advanced generator. threaded = true uses up to Threads.nthreads() tasks; the default uses one. Values and draw consumption follow normal_fill! exactly.
TandemRNG.randexp_next — Function
randexp_next(rng, T = Float64) -> (e, rng′)
randexp_next(rng, [T,] dims::Dims; threaded::Bool = false) -> (A, rng′)
randexp_next(rng, [T,] n::Integer, dims::Integer...; threaded::Bool = false)Draw native standard exponentials through exponential_next, or allocate and fill an array through randexp_next!. T is Float32 or Float64; the default is Float64. Allocating forms use the generator's device binding and the native CPU exponential fill. Values and draw consumption retain the Appendix A rules.
TandemRNG.randexp_next! — Function
randexp_next!(rng, destination; threaded::Bool = false) -> (destination, rng′)Fill a Float32 or Float64 CPU destination through exponential_fill! and return the same destination with the advanced generator. threaded = true uses up to Threads.nthreads() tasks; the default uses one. Values and draw consumption follow exponential_fill! exactly.
Derived draws
TandemRNG.rand_below_next — Function
rand_below_next(rng, n::Union{UInt32,UInt64}) -> (x, rng′)Draw x uniform on 0:n-1 by Lemire's method on draws of the width of n's type, as tandem-c's tandem_u32_below and tandem_u64_below. n = 0 returns 0 after one draw.
TandemRNG.rand_below_fill! — Function
rand_below_fill!(rng, A::AbstractArray{U}, n::U) -> rng′ (U = UInt32 or UInt64)Fill A with integers uniform on 0:n-1, as tandem-c's tandem_fill_u32_below and tandem_fill_u64_below. Element i maps draw i of rand_fill!(rng, A) and the fill consumes exactly length(A) draws. A rejected draw retries on the fallback generator splitrng index g of subrng(P_w) of the key at position 0, where g is the draw's index in the stream (Appendix A of the specification). A fill cut at any element boundary therefore equals the whole fill. An empty fill leaves the position unchanged.
TandemRNG.normal_next — Function
normal_next(rng, T = Float64) -> (z, rng′)A standard normal of type Float32 or Float64 (Appendix A of the specification), element 1 of normal_fill!. A Float64 normal is the 1024-layer ziggurat of one UInt64 draw. A Float32 normal is the cosine half sqrt(-2 log(1 - a)) cos(2πb) of the Box-Muller pair of two Float32 draws a and b, computed in Float32. The values equal tandem-c's tandem_normal_f64 and tandem_normal_f32 bit for bit.
TandemRNG.normal_fill! — Function
normal_fill!(rng, A::AbstractArray{T}; nthreads = Threads.nthreads()) -> rng′Fill A with standard normals (Appendix A of the specification). T is Float32 or Float64. The values equal tandem-c's tandem_fill_normal_f64 and tandem_fill_normal_f32 bit for bit. Threads split the fill as in rand_fill! without changing the values.
- Float64: element
iis the 1024-layer ziggurat of UInt64 drawi, and the fill consumesndraws. A draw that misses the inner rectangles continues on its own fallback generator, keyed by the draw's index in the stream, so a fill cut at any element equals the whole fill. An empty fill aligns the position to 64 bits. - Float32: elements
2j − 1and2jare the cosine and sine halves of the Box-Muller pair of uniform draws2j − 1and2jofrand_fill!(rng, A). The fill consumes2·cld(n, 2)uniforms: an oddnwrites only the cosine half of its last pair. An empty fill leaves the position unchanged.
TandemRNG.exponential_next — Function
exponential_next(rng, T = Float64) -> (e, rng′)A standard exponential -log(1 - u) of type Float32 or Float64 from one uniform draw u of type T (Appendix A of the specification), with the polynomial logarithm of the normals. It equals element 1 of exponential_fill!.
TandemRNG.exponential_fill! — Function
exponential_fill!(rng, A::AbstractArray{T}; nthreads = Threads.nthreads()) -> rng′Fill A with standard exponentials: element i is -log(1 - u) for uniform draw i of rand_fill!(rng, A) (Appendix A of the specification). T is Float32 or Float64. The fill consumes length(A) uniforms. An empty fill leaves the position unchanged. Threads split the fill as in rand_fill! without changing the values.
TandemRNG.ChoiceTable — Type
ChoiceTable(weights::AbstractVector{<:Real})Alias table for drawing an index i in 1:m with probability proportional to weights[i] (Appendix C of the specification). Each weight is converted to Float64. The weights must be finite and not negative, at least one must be positive, and 1 ≤ m < 2^32. Other input throws an ArgumentError. The build uses exact integer arithmetic and consumes no draws, so the table equals tandem-c's tandem_choice_build.
Fields: capacity is the column capacity S, cut[j] the part of column j kept by j, and alias[j] the one-based index that takes the rest.
TandemRNG.choice_next — Function
choice_next(rng, t::ChoiceTable) -> (i, rng′)Draw a one-based index i from the alias table t (Appendix C of the specification). The draw consumes one UInt64 draw and equals element 1 of choice_fill! and tandem-c's tandem_choice plus 1.
TandemRNG.choice_fill! — Function
choice_fill!(rng, A::AbstractArray{<:Integer}, t::ChoiceTable; nthreads = Threads.nthreads()) -> rng′Fill A with one-based indices drawn from the alias table t, as tandem-c's tandem_fill_choice plus 1. Element i comes from UInt64 draw i of the plain fill, so the fill consumes length(A) draws and a fill cut at any element equals the whole fill. An empty fill aligns the position to 64 bits. The element type of A must hold the number of weights. Threads split the fill as in rand_fill! without changing the values.
Child streams
TandemRNG.splitrng — Function
splitrng(rng, n) -> Vector{Tandem8x32{K}}
splitrng(rng, Val(N)) -> NTuple{N,Tandem8x32{K}}
splitrng(rng) -> two childrenChildren of rng by index. Child i (0-based) has key half i & 1 of F(key, i >> 1, SPLIT). Derivation reads only the parent key and starts every child at position zero, so an advanced parent gives the same children. Use stable indices. Child keys have 128 bits; derivation does not guarantee collision-free keys.
TandemRNG.forkrng — Function
forkrng(rng, n) -> (rng′, children::Vector)
forkrng(rng, Val(N)) -> (rng′, children::NTuple)
forkrng(rng) -> (rng′, child)Children of rng at its current block, and the parent moved to the start of the next block. All children of one call use the same block b = position >> 7: child i has key half i & 1 of F(key, b, FORK, aux = i >> 1). Successive calls using the returned parent use distinct blocks. Child-key collisions remain possible. n is at most 2^33.
TandemRNG.subrng — Function
subrng(rng, purpose) -> Tandem8x32{K}One child for a stable integer purpose, from F(key, purpose, FOLD). Use fixed purpose ids for independent roles. purpose is reduced modulo 2^64.
Random wrapper
TandemRNG.Stateful — Type
Stateful(rng::Tandem8x32) <: Random.AbstractRNG
Stateful(seed::Integer)
Stateful{K}(seed::Integer)Mutable wrapper for the Random API. It explicitly rebinds device generators to the CPU. Tandem8x32(st) and parent(st) return the current immutable CPU generator.
TandemRNG.StatefulRNG — Type
StatefulRNG(rng::Tandem8x32)
StatefulRNG(seed::Integer)
StatefulRNG{K}(seed::Integer)Alias for Stateful, retaining Tandem's native sampling algorithms.