Experiment Rules
Experiment layout, identities, shared helpers, Pingstore and SNNLab evidence.
Experiment Rules
1. File layout
-
Use this layout;
dataset.pyis optional, mainly for training:experiments/expXXX/ README.md # goals, then CLI files and arguments recipe.py # scientific settings and network definitions dataset.py # data loading and preprocessing compute.py # generation, simulation, inference and training analyse.py # validation, measurements and estimators present.py # figures, tables and report-ready outputs test.py # all experiment-specific tests custom/ # extensions and necessary execution adapters report/ report.mdx … # supporting article files and authored assets -
Keep CLI entry points small, scientific settings in
recipe.py, and imports free of execution side effects. The canonical stage files own their stage’s implementation, including simulation, measurements and plotting helpers. -
Limit root-level Python files to those above. Minimise
custom/: retain genuine extensions, native lifecycle adapters and necessary execution support there. Do not split ordinary recipe, compute, analyse or present code intocustom/merely to shorten the canonical files. Put data loading and preprocessing indataset.pywhere applicable. -
Keep all experiment tests in one
test.py, not separatetest_*.pyfiles. -
Limit
README.mdto brief goals and CLI files with required and optional arguments.
2. Structure and readability
- Important: Keep only code, tests and execution support needed to reproduce or validate the report and its evidence. Remove obsolete, unused and speculative paths.
- Follow
experiments/exp141for readable, idiomatic structure and style. - Keep
recipe.pycentred onconfiguration(...)andauthor_network(...); the latter defines the network and training specification and returns a compiled SNNLab bundle. Keep loading and execution elsewhere; use small helpers for clarity. - Put each argument on its own line in long calls and definitions. Use descriptive names, consistent indentation and blank lines between meaningful steps.
- Start each module with a concise two-to-three-paragraph docstring covering its scientific purpose, responsibilities, usage and relevant inputs and outputs.
- Comment scientific meaning, units, assumptions and non-obvious API choices beside the code. Introduce meaningful steps without narrating obvious Python operations.
3. Configuration
- Define a plain
configuration(...)inrecipe.pyreturning a fresh dictionary of resolved scientific settings. Keep defaults together in a readabledict(...). - Use explicit arguments for supported overrides; validate protocol names and values.
- Include architecture, data selection, preprocessing, numerical and training settings, recording choices and interpretation where applicable. State units in names and explain non-obvious semantics.
- Resolve defaults and overrides before deriving dependent values. Copy mutable constants so calls do not share configuration state.
- Route CLI overrides through
configuration(...); do not scatter scientific defaults or silently patch the returned dictionary. - Execute from and record the resolved configuration; keep derived values consistent with the settings used.
4. Identity and dependencies
- Allocate a free, zero-padded
expXXXnever used before, including deleted experiments. Check directories, Git history and Pingstore allocation records. - Keep experiment-specific code inside its own directory.
- Keep shared helpers directly in
experiments/; reuse and extend maintained helpers instead of copying their machinery. - Obtain user permission before creating a new shared helper in
experiments/. - Consume other experiments’ data only through explicitly identified, validated Pingstore runs.
- Never import another experiment’s code or
.scratch/code, or depend on pre-existing scratch files.
5. Migrating scratch work to an experiment
- Allocate a never-used ID under Identity and dependencies.
- Adapt required source to the standard layout: settings in
recipe.py, separate execution stages, extra modules incustom/, tests intest.pyand a brief CLI README. - Remove scratch imports, hard-coded scratch paths and local-file dependencies. Reuse maintained helpers and SNNLab; identify scientific inputs explicitly.
- Preserve existing outputs’ original settings and provenance. Follow Pingstore Rules for import or recovery, or generate new runs; moving files does not validate evidence or change its settings.
- Run tests, check side-effect-free imports and verify independence from the original scratch directory. Follow Compute and Pingstore Rules for execution.
- Remove the scratch directory only after verification and preservation of needed source and results. Update migrated report paths and evidence under Writing Rules.
6. SNNLab first
- Use SNNLab’s network, simulation, inference, training, recording, analysis and visualization features where available. Check the installed API before adding custom scientific machinery; do not reimplement equivalent features.
6.1. Network diagrams
- Save one circuit diagram per unique architecture from an actual compiled bundle:
bundle.visualise(diagram_path, view="circuit")(snnlab.lang.Bundle.visualise). - Save the complete bundle separately with
bundle.write(bundle_path, visualise=False). Avoidvisualise=Trueper sweep bundle; it generates all three views in SVG and PNG. - Reuse diagrams across parameter, seed, timestep and sweep changes; generate another only when the network structure changes.
6.2. Network snapshots
-
Capture resolved network settings and parameters with SNNLab’s native snapshots.
-
For training, require end-of-training and best-validation-checkpoint snapshots; use each checkpoint’s actual parameters. A pre-training snapshot is not required.
-
For simulation, including inference, require only a simulation-start snapshot.
-
Pass native snapshots directly to execution calls:
from snnlab.sim import SnapshotSpec # Simulation or inference: snapshot = SnapshotSpec(path=snapshot_path, stage="start", parameters=True) # End of training: snapshot = SnapshotSpec(path=snapshot_path, stage="end", parameters=True) # Pass snapshot=snapshot to simulate(...) or train(...). -
Call keyword-only
simulate(...)andtrain(...)directly, spelling out arguments without request-dictionary unpacking or wrappers.ExecutionSpecis internal since 0.21;simulatehandles inference since 0.23, wheninferwas removed. -
Group optimizations in
Performance(...); retain requested policy in the recipe and native resolved performance metrics.SNNLAB_DEVICEoverrides automatic selection. -
Use distinct snapshot paths per execution and retain files in Pingstore. Snapshots preserve metadata and parameters; they are not resumable checkpoints.
-
Bind report parameter tables to native snapshots and recorded configuration under Writing Rules. Keep formatting in reports; do not duplicate parameter payloads or add parameter-table serializers.
-
For native calls, write the bundle locally with
visualise=Falseand let SNNLab capturesnapshot=SnapshotSpec(...). For custom loops, keep the native lifecycle adapter incustom/; never duplicate metadata or parameter serialization.
7. Parallel compute
- Follow
experiments/exp124/compute.pyfor independent work on one multi-GPU host, including Modal. - Define work items with stable IDs and device allocations. Validate resources before loading data and divide CPU threads between workers.
- Use one worker function for sequential and parallel work. Prepare shared inputs once and verify their checksums before and after execution.
- Give workers disjoint exports and scratch files. Use
hpc.concurrent_compute.run_active_workersfor spawned processes, completion records, output-hash validation and cancellation on failure. - Give one coordinator ownership of
stage_run, output validation and merging, execution provenance and run completion. Workers must not edit its manifest. - Expose
--parallel; Modal supplies resources and forwards it within one invocation. The helper is non-resumable. Follow Compute Rules for dispatch and Pingstore Rules for storage.
8. Saving and consuming results
-
Use Pingstore for scientific runs, provenance and outputs; follow Pingstore Rules (
md/notes/pingstore-rules.md). -
Create runs with
pingstore.library.stage_run; validate explicit inputs withsource_run. -
Export scientific outputs through
StageRun.export; useStageRun.scratchfor temporary work. -
Record configuration and input provenance in the run record, not parallel manifests.
-
Invoke compute, analyse and present independently. Never select “latest” or automatically launch an upstream stage.
-
Treat completed runs as immutable; changed results require a new run.
-
Completed presentations become current through the shared reporting resolver. Reports follow their exact input ancestry; this does not change scientific input selection. Inspect
pingstore statusfor current evidence and retention reasons. Stage completion never implicitly prunes history. -
For future exports, retain the native bundle, final-training and best-validation parameter snapshots (or simulation-start snapshots), and consolidated data required by downstream analysis and reproduction. Keep temporary datasets, duplicate bundles and unneeded intermediate diagnostics in scratch. Retain additional snapshots or optimizer/RNG state only for an explicit scientific consumer or continuation requirement; existing completed runs stay immutable.
-
The normal lifecycle is compute → analyse → present → validate → back up → publish → prune. Backup and publication are distinct. Cleanup defaults to both stores and requires an inspected, hash-bound plan; interrupted operations resume their original journal. See Pingstore Rules for deployment retention.