This package intends to provide a simple RNG with stable streams, suitable for tests in packages which need reproducible streams of random numbers across Julia versions. Indeed, the Julia RNGs provided by default are documented to have non-stable streams (which for example enables some performance improvements).
The StableRNG
type provided by this package strives
for stability, but if bugs which require breaking this promise are found,
a new major version will be released with the fix.
StableRNG
is currently an alias for LehmerRNG
, and implements a well understood
linear congruential generator (LCG); an LCG is not state of the art,
but is fast and is believed to have reasonably good statistical properties [1],
suitable at least for tests of a wide range of packages.
The choice of this particular RNG is based on its simplicity, which limits
the chances for bugs.
Note that only StableRNG
is exported from the package, and should be the only
type used in client code; LehmerRNG
might be renamed, or might be made a distinct
type from StableRNG
in any upcoming minor (i.e. non-breaking) release.
Currently, this RNG requires explicit seeding (in the constructor
or via Random.seed!
), i.e. no random seed will be chosen for the user
as is the case in e.g. MersenneTwister()
.
The stable (guaranteed) API is
- construction:
rng = StableRNG(seed::Integer)
(in particular the aliasLehmerRNG
is currently not part of the API) - seeding:
Random.seed!(rng::StableRNG, seed::Integer)
(with0 <= seed <= typemax(UInt64)
) rand(rng, X)
whereX
is any of the standard bitInteger
types (Bool
,Int8
,Int16
,Int32
,Int64
,Int128
,UInt8
,UInt16
,UInt32
,UInt64
,UInt128
)rand(rng, X)
,randn(rng, X)
,randexp(rng, X)
whereX
is a standard bitAbstractFloat
types (Float16
,Float32
,Float64
)- array versions for these types, including
the mutating methods
rand!
,randn!
andrandexp!
rand(rng, ::AbstractArray)
(e.g.rand(rng, 1:9)
); the streams are the same on 32-bits and 64-bits architecturesshuffle(rng, ::AbstractArray)
andshuffle!(rng, ::AbstractArray)
Note that the generated streams of numbers for scalars and arrays are the same,
i.e. rand(rng, X, n)
is equal to [rand(rng, X) for _=1:n]
for a given rng
state.
Please open an issue for missing needed APIs.
[1] LehmerRNG
is implemented after the specific constants published by
Melissa E. O'Neill in this
C++ implementation,
and passes the Big Crush test (thanks to Kristoffer Carlsson for running it).
See also for example this
blog post.
In your tests, simply initialize an RNG with a given seed, and use it instead of the default provided one, e.g.
rng = StableRNG(123)
A = randn(rng, 10, 10) # instead of randn(10, 10)
@test inv(inv(A)) ≈ A