Experiment Rules
Experiment layout, identities, shared helpers, Pingstore and SNNLab evidence.
Experiment Rules
Execution permission
- Do not run simulations by default. Start a simulation only when the user explicitly requests it or grants permission for that execution. This includes local and remote runs, training, inference, sweeps, diagnostic simulations and simulation-backed tests.
- Requests to edit code, recipes, diagrams, tables or prose do not authorize simulation execution. Do not launch a run to validate an edit, refresh figures or complete a lifecycle unless that execution is authorized.
- Existing authorization applies within its requested scope; do not ask again for an already authorized run. Ask before expanding to additional runs or a larger sweep. If execution is not authorized, finish the requested edits and state briefly that no simulation was run.
- Static inspection, bundle compilation for diagrams and tests that do not run simulations may proceed as appropriate to the task. Do not use these routes to bypass the simulation permission requirement.
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 (
notes/rules/pingstore.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 full lifecycle is compute → analyse → present → validate → back up → publish → prune. This describes stage order, not permission to execute every stage; perform only the stages authorized by the task. 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.