Skip to content

Extend a model, task or analysis

An extension should make a new question possible without changing an existing result’s meaning. Begin with a model parameter or task option when that expresses the intended change. Add a new substrate only when its equations differ.

Choose the smallest implementation

IntentCurrent path
Change a supported modelNodeSpec.parameters
Change task timing or observationsTaskSpec.options
Change node count or input scaleCompositionSpec
Compare a mechanismNamed targets and declared interventions
Explore parametersDevelopment-only sweep
Add a diagnosticPure offline analysis over owned result data
Add a new model or task familyReviewed package implementation and resolver changes

There is no general third-party registry or arbitrary body assembly in this release. Earlier extension methods remain available at their source revision; see version history.

Read equations and tests together

Terminal window
uv sync --locked --group dev
uv run pytest python/tests/test_sorn.py python/tests/test_task_validation.py

Use these focused suites to understand construction, update ordering, pruning, reset and task schedule validation. A passing existing suite does not validate a new equation.

Scientific specifications remain immutable host values. Keep device buffers and memory admission in execution. Use explicit generators and stable entity/feature IDs. Preserve destination/source matrices and fixed ndarray ranks with runtime dimensions.

Try a configuration change first

from brainlesslab import (
CompositionSpec, EvaluationSpec, NodeSpec, TaskSpec, profile, resolve,
)
plan = profile(
CompositionSpec(
NodeSpec("sorn", {"eta_stdp": 0.0}),
TaskSpec("delayed_cue", {"delays": (128,)}),
count=32,
),
evaluation=EvaluationSpec(blocks=2, trials_per_block=2, root_seed=63),
)
print(resolve(plan).targets[0])

Expect one resolved SORN target. This disables SORN’s STDP increment from construction while retaining intrinsic plasticity and enabled weight normalisation. It is not the same intervention as freezing weights after a period of adaptation. Check implemented meanings before using a parameter as a causal control.

Separate construction from stepping

An implemented model has an immutable configuration, a host-side construct function, an owned InitialState and a ModelBatch with device arrays. Construction receives an explicit NumPy generator; it must not consume an unrelated task stream.

A batch exposes step(inputs, noise, active), emitted activity, effectors and finite flags. It supports reset, weight freeze, full-plasticity freeze and owned snapshots. Use ordered destination-owned sums instead of floating-point atomics.

Keep task labels out of the online controller. A new task needs validated options, ports, timing, a prepared world and a native scoring rule. If it uses feedback, distinguish available observations from diagnostic truth retained for scoring.

Define the new contract before integrating it

For a model, state the old-state/new-state order, threshold equality rule, emitted activity, readout, adaptation, frozen modes and reset. Test small hand-calculated states with an independent NumPy reference. Include non-finite drives, empty connectivity, inactive slots, owned snapshots and batch independence.

For a task, state available observations, action cadence, response windows, ties, termination, native units and scoring opportunity. Validate initial schedules before device allocation. Keep labels and hidden task truth outside the online controller. Choose an appropriate reference policy without calling its observed score an upper bound.

For offline analysis, preserve the independent unit and prevent fit/validation leakage. For a backend change, qualify the arithmetic/device path separately from model equations.

Only after those checks should you profile runtime costs or propose an optimisation. Preserve a single equation source when introducing fused execution. Canonical ndarray access and explicit returned status values avoid stale reads across aliased helper views.

Build a reviewable extension

Run focused tests first, then the package and site gates. Execute examples referenced by the guide. A frozen dataclass alone does not own mutable array storage: check ownership and batch independence explicitly. Keep backend qualification separate from equation conformance, and retain compilation, transfer and recording costs when measuring performance.

The active package supports two model families and ten task families. A Python class alone does not make a supported NodeSpec or TaskSpec kind; discovery and resolution need a reviewed package change. Evolution, durable checkpoint/resume and autodiff are not implemented.

Continue with interfaces, learning exercises and runtime performance.