ConceptStablesince v0.9.2

Reproducibility

The exact contract NeuronScope makes about repeated runs — and, just as importantly, what it explicitly does not.

Audience: Anyone running experimentsRead: 8 minEdit on GitHub

Reproducibility is a scoped promise, not a wish. NeuronScope defines it precisely so a failure to reproduce is a bug in a specific stage, not a mystery.

The contract

Given the same fingerprinted model, dataset, prompt, and environment, two recordings produce byte-identical artifacts under the same NeuronScope major version. Two artifacts are considered "the same experiment" if and only if their input fingerprints match.

What is guaranteed

  • Fingerprints are stable across machines and OSes for a given canonicalizer version.
  • The artifact schema is versioned; a reader can decode any artifact from a supported major version without loss.
  • Random seeds set through ns.seed_all(...) propagate to every framework the recorder touches.
  • The order of observations in an artifact is deterministic — it follows the recording plan, not process scheduling.

What is not

  • Non-deterministic ops (e.g. torch.use_deterministic_algorithms(False)) are not corrected — they will produce different tensor bytes.
  • Data races in user code are not detected. Fingerprints only prove input identity.
  • Framework upgrades across a minor version can change the environment fingerprint. This is intentional; results may legitimately differ.

A reproducible run

  1. Pin your framework version. uv.lock or a requirements.txt is enough.
  2. Call ns.seed_all(0) before touching any random source.
  3. Record with deterministic=True — this refuses to run if an installed op is known non-deterministic.
  4. Commit the resulting .nsz to a reference location (git-LFS or a cloud bucket).
  5. In CI, re-record and assert ns.assert_matches(new_artifact, reference).

Common failure modes

  • Silent driver upgrade. CUDA 12.1 → 12.4 changes the environment fingerprint. Detected automatically.
  • Non-deterministic dataloader. A shuffled dataset with a missing seed will fingerprint differently every run. Use ns.Dataset.from_iter(..., shuffle_seed=0).
  • In-place mutation. Mutating a model between fingerprint and record invalidates the fingerprint. Fingerprint immediately before recording.
Related