Reproducibility

Definitions

Given fixed inputs, all items (should) fall into one of three categories:

  • Output is non-deterministic, thus never reproducible (example: rand::thread_rng)
  • Output is deterministic, but not considered portable (SmallRng, StdRng; limitations below)
  • Output is deterministic and portable (named RNGs; most distributions, sampling and shuffling algorithms)

In general, functionality is considered deterministic and portable unless it is clearly non-deterministic (e.g. getrandom, ThreadRng) or it is documented as being nonportable (e.g. StdRng, SmallRng).

Crate versions

We try to follow semver rules regarding API-breaking changes and MAJOR.MINOR.PATCH versions:

  • New patch versions should not include API-breaking changes or major new features
  • Before 1.0, minor versions may include API breaking changes. After 1.0 they should not.

Additionally, we must also consider value-breaking changes and portability. When given fixed inputs,

  • For non-deterministic items, implementations may change in any release
  • For deterministic nonportable items, output should be preserved in patch releases, but may change in any minor release (including after 1.0)
  • For portable items, output should be preserved by patch releases. Minor releases (including after 1.0) may include such value-breaking changes, though these must be documented in the CHANGELOG.

Testing

We expect all pseudo-random algorithms to test the value-stability of their output, where possible:

  • PRNGs should be compared with a reference vector (example)
  • Other algorithms should include their own test vectors within a value_stability test or similar (example)

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.

Within Rand we adhere to this rule whenever possible. All sequence-related code requiring a bounded usize value will sample a u32 value unless the upper bound exceeds u32::MAX. (Note that this actually improves benchmark performance in many cases.)

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 aleviate (or further complicate) this concern, we prefer to use libm over std implementations of these transcendental functions. See rand_distr features.