Reproducibility
The rust-random
libraries make limited commitments to reproducibility of seedable PRNGs and stochastic algorithms.
This chapter concerns value-stability of deterministic processes using the rust-random
libraries.
API-breaking, value-breaking and SemVer
A change (to a library) is considered API-breaking if it may cause a compilation failure of code which was compatible with a prior version of the API, or is otherwise an incompatible change.
We aim to follow SemVer rules regarding API-breaking changes and MAJOR.MINOR.PATCH
versions. That is, post 1.0, new minor versions should not introduce API-breaking changes.
A change is considered value-breaking if it is not API-breaking yet would result in changed output values of a deterministic stochastic process using only unchanged parts of the rust-random
API.
Value-breaking changes are permitted in minor versions.
Non-portable deterministic items
An item in a rust-random
API (such as a struct or function) may be declared to be non-portable, meaning that it opts out of all reproducibility guarantees. Non-portable items may be deterministic, yet yield different results on different platforms and library versions (they may make value-breaking changes in any release).
This is a change in policy affecting rand
from version 0.10
or 1.0
(whichever release is next); up to version 0.9
non-portable items were not permitted to make value-breaking changes in patch releases.
This non-portable declaration must be clearly mentioned in documentation. The following items make such a declaration:
Portable items
Some items are clearly non-deterministic (e.g. rand::rng
). Some items are deterministic but non-portable (above). All other parts of the public API of rust-random
crates (including PRNGs, distributions and other stochastic algorithms) are expected to be portable:
- Results should be reproducible across platforms
- Results should be reproducible across patch releases
- Minor releases, including after 1.0, may make value-breaking changes to portable items. Such changes must be well motivated and should be clearly mentioned in the CHANGELOG.
Testing
We expect all portable stochastic algorithms to test the value-stability of their output with some form of test vector.
- PRNGs should test against a reference vector where available (example)
- Other algorithms should include their own test vectors within a
value_stability
test or similar (example)
Support for prior versions
We aim to support users of rust-random
crates using a prior MAJOR.MINOR
version for the purposes of reproducibility by:
- Providing security fixes as patch versions where appropriate
- Facilitating the back-porting of compatible additions from future crate versions on request
- Other fixes may be considered for back-porting, but are often not possible without API-breaking or value-breaking changes
Limitations
Portability of usize
There is unfortunately one non-portable item baked into the heart of the Rust
language: usize
(and isize
). For example, the size of an empty
Vec
will differ on 32-bit and 64-bit targets. For most purposes this is not an
issue, but when it comes to generating random numbers in a portable manner
it does matter.
A simple rule follows: if portability is required, never sample a usize
or
isize
value directly.
From rand v0.9
, isize
and usize
types are no longer supported in many parts of the public API, including StandardUniform
. usize
is supported by SampleUniform
and thus Rng::random_range
, using u32
sampling whenever possible to maximise portability.
Portability of floats
The results of floating point arithmetic depend on rounding modes and
implementation details. In particular, the results of transcendental functions vary
from platform to platform. Due to this, results of distributions in rand_distr
using f32
or f64
may not be portable.
To alleviate (or further complicate) this concern, we prefer to use libm
over std
implementations of these transcendental functions. See rand_distr features.