numerical-determinism
Seeds pin the draws; they do not pin the arithmetic. Parallel reductions reorder
floating-point sums, GPU atomics race, and optimization engines take time-dependent paths —
so "same seed, different number" is expected behavior until execution is pinned too. This
skill sets the pinning knobs, defines honest reproducibility tiers, and (folded in) the
hardware-context record every run should carry.
When to use
- A seeded computation gives different results across runs or machines.
- A claim needs bit-reproducibility (regression pins, cross-checking two implementations).
- Any timing/performance number is about to be reported.
- Setting up the meta-file fields for a new project.
When NOT to use
- Variation you want — spread across seeds/realizations is the object of study
(
statistical-reporting); do not pin it away.
- Version-level differences (library upgrades changing algorithms) —
run-provenance.
- Recording which seeds were drawn —
run-provenance.
Sources of nondeterminism (check in this order)
- Thread-count drift — BLAS/OpenMP pools sized by the machine. Pin explicitly
(e.g.
OMP_NUM_THREADS, MKL_NUM_THREADS, or single-threaded mode for pinned runs).
- Parallel reduction order — float addition is not associative; multi-threaded sums
differ run to run. Pinning threads to 1, or using deterministic-reduction options where
the library offers them, removes it.
- Hash/iteration order — order-dependent iteration over hashed containers (pin the
hash seed, e.g.
PYTHONHASHSEED, or sort before iterating).
- GPU kernels — atomics and autotuned algorithm selection; enable the framework's
deterministic mode for pinned runs and accept the speed cost.
- Engine-level nondeterminism — optimization/simulation engines with parallel search:
the incumbent found can depend on thread scheduling and wall-clock limits. Pin the
engine's thread count and seed parameters; prefer deterministic work-limit options over
wall-clock limits where the engine offers them; otherwise report engine results as
tolerance-reproducible, not bit-reproducible.
Reproducibility tiers (state which one a claim uses)
- Tier A — bit-reproducible: same machine, locked environment, pinned threads/kernels.
Required for regression pins and cross-implementation parity checks
(
correctness-gate).
- Tier B — tolerance-reproducible: same environment class, numbers agree within a
stated tolerance. The realistic tier for parallel/GPU/engine runs; the tolerance is part
of the claim.
- Tier C — statistically reproducible: distributions agree across replications
(
statistical-reporting). The tier most scientific claims actually need.
Chasing Tier A where Tier C suffices wastes compute; claiming Tier A while delivering
Tier B corrupts regression baselines. Name the tier.
Hardware context (folded field-set — record into meta.json)
Every kept run records the machine it ran on; timing claims are meaningless without it and
numeric differences are undiagnosable without it:
"hardware": {
"cpu_model": "...", "cores_physical": 0, "ram_gb": 0,
"gpu_model": "... or null", "gpu_driver": "... or null",
"os": "...", "arch": "...", "threads_pinned": "OMP=1,ENGINE=1 or 'unpinned'"
}
(Schema: templates/results-meta.schema.json. Architecture-native verification is
run-provenance's preflight; the field lands here.)
Rules
- Pinned runs declare their pins — thread settings and deterministic-mode flags go in
the meta file next to the seeds; an undeclared pin is an unreproducible pin.
- Timing numbers always ship with hardware context and thread settings. "2.3× faster"
on unstated hardware is marketing, not measurement.
- Cross-machine comparisons compare Tier B or C, never raw bits; put the tolerance in
the text.
- Nondeterminism found ≠ nondeterminism fixed. If a pinned run still varies, that is a
finding to record and diagnose (
negative-results-ledger if abandoned), not to rerun
until it looks stable.
Configuration
{{evidence_dir}} — meta files carrying pins + hardware context.
{{gate_command}} — the gate whose pins depend on Tier A holding.
Provenance & maintenance
Authored from the numerical-computing canon (non-associative float reduction, GPU
determinism modes, engine scheduling effects), grounded in working practice: campaign
preflights that verify native architecture, meta files recording an
architecture-verified flag, and engine runs treated as tolerance-reproducible with the
tolerance pinned in a correctness gate. Hardware-context is a documented field-set here by
design — not a separate skill. See the pack's examples/ directory for the worked exemplar
mapping.
Re-verify in your project:
python -c "import os; print(os.environ.get('OMP_NUM_THREADS'))" — pins are actually set
in the run environment (adapt variable names).
- Run a pinned computation twice and
diff the outputs — Tier A holds where claimed.
grep -h "hardware\|arch" {{evidence_dir}}/*/meta.json | head -3 — runs record the
machine.
1---2name: numerical-determinism3description: Use when the same code and same seed still produce different numbers — pinning thread counts, parallel reduction order, GPU kernels, and engine-level nondeterminism — and when a claim requires bit-reproducibility or a timing result requires hardware context. Also defines the hardware-context fields every run's meta file should record. Trigger phrases: "same seed different result", "pin the threads", "deterministic mode", "results differ across machines", "record the hardware", "timing needs the machine specs".4---56# numerical-determinism78Seeds pin the *draws*; they do not pin the *arithmetic*. Parallel reductions reorder9floating-point sums, GPU atomics race, and optimization engines take time-dependent paths —10so "same seed, different number" is expected behavior until execution is pinned too. This11skill sets the pinning knobs, defines honest reproducibility tiers, and (folded in) the12hardware-context record every run should carry.1314## When to use1516- A seeded computation gives different results across runs or machines.17- A claim needs bit-reproducibility (regression pins, cross-checking two implementations).18- Any timing/performance number is about to be reported.19- Setting up the meta-file fields for a new project.2021## When NOT to use2223- Variation you *want* — spread across seeds/realizations is the object of study24 (`statistical-reporting`); do not pin it away.25- Version-level differences (library upgrades changing algorithms) — `run-provenance`.26- Recording which seeds were drawn — `run-provenance`.2728## Sources of nondeterminism (check in this order)29301. **Thread-count drift** — BLAS/OpenMP pools sized by the machine. Pin explicitly31 (e.g. `OMP_NUM_THREADS`, `MKL_NUM_THREADS`, or single-threaded mode for pinned runs).322. **Parallel reduction order** — float addition is not associative; multi-threaded sums33 differ run to run. Pinning threads to 1, or using deterministic-reduction options where34 the library offers them, removes it.353. **Hash/iteration order** — order-dependent iteration over hashed containers (pin the36 hash seed, e.g. `PYTHONHASHSEED`, or sort before iterating).374. **GPU kernels** — atomics and autotuned algorithm selection; enable the framework's38 deterministic mode for pinned runs and accept the speed cost.395. **Engine-level nondeterminism** — optimization/simulation engines with parallel search:40 the incumbent found can depend on thread scheduling and wall-clock limits. Pin the41 engine's thread count and seed parameters; prefer deterministic work-limit options over42 wall-clock limits where the engine offers them; otherwise report engine results as43 tolerance-reproducible, not bit-reproducible.4445## Reproducibility tiers (state which one a claim uses)4647- **Tier A — bit-reproducible**: same machine, locked environment, pinned threads/kernels.48 Required for regression pins and cross-implementation parity checks49 (`correctness-gate`).50- **Tier B — tolerance-reproducible**: same environment class, numbers agree within a51 stated tolerance. The realistic tier for parallel/GPU/engine runs; the tolerance is part52 of the claim.53- **Tier C — statistically reproducible**: distributions agree across replications54 (`statistical-reporting`). The tier most scientific claims actually need.5556Chasing Tier A where Tier C suffices wastes compute; claiming Tier A while delivering57Tier B corrupts regression baselines. Name the tier.5859## Hardware context (folded field-set — record into `meta.json`)6061Every kept run records the machine it ran on; timing claims are meaningless without it and62numeric differences are undiagnosable without it:6364```json65"hardware": {66 "cpu_model": "...", "cores_physical": 0, "ram_gb": 0,67 "gpu_model": "... or null", "gpu_driver": "... or null",68 "os": "...", "arch": "...", "threads_pinned": "OMP=1,ENGINE=1 or 'unpinned'"69}70```7172(Schema: `templates/results-meta.schema.json`. Architecture-native verification is73`run-provenance`'s preflight; the field lands here.)7475## Rules76771. **Pinned runs declare their pins** — thread settings and deterministic-mode flags go in78 the meta file next to the seeds; an undeclared pin is an unreproducible pin.792. **Timing numbers always ship with hardware context and thread settings.** "2.3× faster"80 on unstated hardware is marketing, not measurement.813. **Cross-machine comparisons compare Tier B or C**, never raw bits; put the tolerance in82 the text.834. **Nondeterminism found ≠ nondeterminism fixed.** If a pinned run still varies, that is a84 finding to record and diagnose (`negative-results-ledger` if abandoned), not to rerun85 until it looks stable.8687## Configuration8889- `{{evidence_dir}}` — meta files carrying pins + hardware context.90- `{{gate_command}}` — the gate whose pins depend on Tier A holding.9192## Provenance & maintenance9394Authored from the numerical-computing canon (non-associative float reduction, GPU95determinism modes, engine scheduling effects), grounded in working practice: campaign96preflights that verify native architecture, meta files recording an97architecture-verified flag, and engine runs treated as tolerance-reproducible with the98tolerance pinned in a correctness gate. Hardware-context is a documented field-set here by99design — not a separate skill. See the pack's `examples/` directory for the worked exemplar100mapping.101102Re-verify in your project:103104- `python -c "import os; print(os.environ.get('OMP_NUM_THREADS'))"` — pins are actually set105 in the run environment (adapt variable names).106- Run a pinned computation twice and `diff` the outputs — Tier A holds where claimed.107- `grep -h "hardware\|arch" {{evidence_dir}}/*/meta.json | head -3` — runs record the108 machine.