Skip to main content
Never roll a dice if you don’t already know the answer.
Use Random utilities when generated rows, samples, or vectors should stay stable across runs. StableRNG gives you six stability guarantees: same seed → same output; new pipeline steps stay isolated; batch and loop calls agree; growing a dataset does not reshuffle old picks; other RNG libraries stay untouched; sample results follow item content, not list order.

1. Why Randomness Needs Stability

Without a deliberate design for stability, “reproducible” random data is fragile. A test that sets seed=42 can still break when someone adds a random call earlier in the pipeline, when the dataset grows by one row, when you switch from a batch call to a loop, or when input order changes. Each change can rewrite every value that came after it. HeavenBase’s StableRNG guarantees six orthogonal stability properties. Each one closes a concrete failure mode you would otherwise hit as an application grows.

1.1. Seed Stability — same seed, same output

What it means: Run the same draw twice with the same seed and you get the same value. Without seed stability, seed=42 might produce different strings, floats, or labels across runs, versions, or machines — so a failing test is hard to replay.
Same seed and same draw order always reproduce the same sequence. That is the baseline for debugging fixtures and benchmarks.

1.2. Process Stability — new steps do not rewrite old ones

What it means: Add a new random step anywhere in your pipeline; values from steps you already rely on stay the same. Without process stability, inserting a “generate metrics” step between “generate rows” and “generate samples” would change both downstream outputs — even though those steps did not change.
The step(...) method derives a named child stream from the base seed without mutating the parent. Two different step names never share state.

1.3. Batch Stability — one batch call matches a loop

What it means: rng.method(..., n=10) returns the same values as calling rng.method(...) ten times in a loop. Without batch stability, refactoring between batched and incremental code changes your fixtures — even with the same seed and method.
This holds for every generation method that accepts n=: rnd_int, rnd_str, rnd_vec, choice, and so on. You can pick the style that reads best without changing results.

1.4. Data Stability — new records do not reshuffle old picks

What it means: Sample or split today; add more items tomorrow; decisions for items that were already in the pool stay the sam (minor changes are expected but not drastic). Without data stability, sampling 2 items from [p1, p2, p3, p4] with seed=42 might select [p3, p4], but adding p5 could change the selection to [p2, p5]. Benchmarks and evaluation splits become brittle.
hash_sample and hash_split base each item’s selection on its own digest (seeded by the StableRNG seed), not on list position. Adding items only changes the decision for the added items. The core idea of achieving data stability is to use a hash of the item to determine the selection, rather than the position of the item in the list. When adding a single new item to the pool, depending on whether its hash value falls within the threshold, it will be added to the selected or remaining group, kicking at most one item out of the selected group.

1.5. Ecosystem Stability — other RNG libraries stay untouched

What it means: Using StableRNG never seeds or resets Python’s random, NumPy’s global RNG, PyTorch, or any other RNG in your process. Without ecosystem stability, heavy StableRNG use could silently re-seed global RNG state and break reproducibility elsewhere in the codebase.
Each StableRNG creates its own independent NumPy Generator instance. It never touches the global random module, numpy.random state, or any other RNG system.

1.6. Hash Stability — content decides, not list order

What it means: Put the same items in a different order; the sample or split for those items stays the same. Without hash stability, sampling from ['p1', 'p2', 'p3'] might give ['p3', 'p1'], but reordering the input to ['p3', 'p1', 'p2'] could give ['p1', 'p2'].
hash_sample and hash_split compute each item’s digest with the generator’s configured hash function (CRC32 by default, optionally md5 or sha256), salt it with the seed, then select by digest value. Input order never enters the decision.
These six properties are built into StableRNG. You get all of them by passing a seed — no extra flags or setup.

2. Core Idea

StableRNG is for tests, demos, benchmarks, and LLM-application fixtures where “random” data still needs to be debuggable. If a failure happens with seed 42, you should be able to regenerate the same rows, vectors, and samples later.

3. Generate a Reproducible Sequence

Create a generator with a seed and use it as a context when multiple draws belong to one sequence.
The same seed and draw order produce the same sequence:
Outside a context, a single method call is stable for the current seed. That is useful when one value should be reproducible without coupling it to previous draws.

4. Derive Child Streams

Use step(...) to split one base seed into named streams without mutating the parent generator.
This keeps unrelated parts of a test from changing each other when one workflow adds a new random draw.

5. Generate Batches and Vectors

Most generators accept n for a count or shape. Use this for fixtures that need many values at once.
rnd_vec(...) returns unit-length vectors. That makes it useful for LLM and vector-search development, where you often need embeddings-shaped data before wiring a real embedding provider into the example.

6. Sample Without Rewriting the World

Use hash_sample(...) and hash_split(...) when a stable sample should not depend on input order or on unrelated new records.
If you later add "p5", the decision for "p1" through "p4" is still based on each item’s hash and the seed, not on the original list position. CRC32 is the default because sampling collisions only change deterministic sample choice or tie handling. The default reads heavenbase.rng.hash_function and heavenbase.rng.hash_modulo from configuration, and each hash_sample(...) / hash_split(...) call can override the hash function.
HeavenBase benchmarks use seeded generation so row content, samples, and vectors can be reproduced.

Further Exploration

Related resources:
  • Hash - deterministic hashes used by stable sampling.
  • File System - write generated fixtures to local artifacts.
  • Query - where synthetic vector fixtures are often used.