snnlab
API referencesnnlab.lang

snnlab.lang.simulation

Complete declared API of the simulation module, with signatures, data fields, validation and source.

Back to lang reference

Simulation recipes describe structured spike sources, background conductances, rate weather, afferent waves and schedules. compile(simulation=...) stores a graph-bound simulation.json. These records do not produce samples until realized by the simulator.

The signatures, defaults, fields, docstrings and implementation excerpts below are generated from the Python source. Annotations are shown as declared; unannotated means the source supplies no type annotation. These pages document callable surfaces, including legacy support utilities, without promising backend support for every declaration.

ShotNoise

View source

Private shot-noise channel with rate in Hz, conductance amplitude and decay time in milliseconds.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

ShotNoise(rate_hz: float, amplitude: float, tau_ms: float)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
rate_hzfloatrequiredRate in spikes per second.
amplitudefloatrequiredStored member of this data contract; see the class docstring and serialization methods.
tau_msfloatrequiredDecay time constant in milliseconds.

ShotNoise.json

View source

def ShotNoise.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'kind': 'shot_noise', 'rate_hz': float(self.rate_hz), 'amplitude': float(self.amplitude), 'tau_ms': float(self.tau_ms)}
Implementation
def json(self) -> dict[str, Any]:
        return {
            "kind": "shot_noise",
            "rate_hz": float(self.rate_hz),
            "amplitude": float(self.amplitude),
            "tau_ms": float(self.tau_ms),
        }
Complete class implementation
class ShotNoise:
    rate_hz: float
    amplitude: float
    tau_ms: float

    def json(self) -> dict[str, Any]:
        return {
            "kind": "shot_noise",
            "rate_hz": float(self.rate_hz),
            "amplitude": float(self.amplitude),
            "tau_ms": float(self.tau_ms),
        }

GlobalShotNoise

View source

Shot-noise specification shared globally across the target population; inherits rate_hz, amplitude and tau_ms from ShotNoise.

Bases: ShotNoise. Inherited third-party framework APIs follow their owning library.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

GlobalShotNoise(rate_hz: float, amplitude: float, tau_ms: float)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
rate_hzfloatrequiredRate in spikes per second.
amplitudefloatrequiredStored member of this data contract; see the class docstring and serialization methods.
tau_msfloatrequiredDecay time constant in milliseconds.

GlobalShotNoise.json

View source

def GlobalShotNoise.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{**super().json(), 'kind': 'global_shot_noise'}
Implementation
def json(self) -> dict[str, Any]:
        return {**super().json(), "kind": "global_shot_noise"}
Complete class implementation
class GlobalShotNoise(ShotNoise):
    def json(self) -> dict[str, Any]:
        return {**super().json(), "kind": "global_shot_noise"}

GroupedShotNoise

View source

Source docstring:

Shot noise shared within contiguous local cell groups.

Bases: ShotNoise. Inherited third-party framework APIs follow their owning library.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

GroupedShotNoise(rate_hz: float, amplitude: float, tau_ms: float, group_size: int = 16)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
rate_hzfloatrequiredRate in spikes per second.
amplitudefloatrequiredStored member of this data contract; see the class docstring and serialization methods.
tau_msfloatrequiredDecay time constant in milliseconds.
group_sizeint16Stored member of this data contract; see the class docstring and serialization methods.

GroupedShotNoise.json

View source

def GroupedShotNoise.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{**super().json(), 'kind': 'grouped_shot_noise', 'group_size': int(self.group_size)}
Implementation
def json(self) -> dict[str, Any]:
        return {
            **super().json(),
            "kind": "grouped_shot_noise",
            "group_size": int(self.group_size),
        }
Complete class implementation
class GroupedShotNoise(ShotNoise):
    """Shot noise shared within contiguous local cell groups."""

    group_size: int = 16

    def json(self) -> dict[str, Any]:
        return {
            **super().json(),
            "kind": "grouped_shot_noise",
            "group_size": int(self.group_size),
        }

CellDistribution

View source

Source docstring:

Reproducible multiplicative cell heterogeneity laws.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

CellDistribution(rate: Spec = field(default_factory=lambda: Spec('constant', {'value': 1.0})), amplitude: Spec = field(default_factory=lambda: Spec('constant', {'value': 1.0})))

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
rateSpecfield(default_factory=lambda: Spec('constant', {'value': 1.0}))Stored member of this data contract; see the class docstring and serialization methods.
amplitudeSpecfield(default_factory=lambda: Spec('constant', {'value': 1.0}))Stored member of this data contract; see the class docstring and serialization methods.

CellDistribution.json

View source

def CellDistribution.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'rate': self.rate.json(), 'amplitude': self.amplitude.json()}
Implementation
def json(self) -> dict[str, Any]:
        return {"rate": self.rate.json(), "amplitude": self.amplitude.json()}
Complete class implementation
class CellDistribution:
    """Reproducible multiplicative cell heterogeneity laws."""

    rate: Spec = field(default_factory=lambda: Spec("constant", {"value": 1.0}))
    amplitude: Spec = field(default_factory=lambda: Spec("constant", {"value": 1.0}))

    def json(self) -> dict[str, Any]:
        return {"rate": self.rate.json(), "amplitude": self.amplitude.json()}

BackgroundChannel

View source

Combine private and shared shot noise with multiplicative cell heterogeneity for one conductance channel.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

BackgroundChannel(private: ShotNoise, shared: GlobalShotNoise | GroupedShotNoise, heterogeneity: CellDistribution = field(default_factory=CellDistribution))

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
privateShotNoiserequiredStored member of this data contract; see the class docstring and serialization methods.
sharedGlobalShotNoise | GroupedShotNoiserequiredStored member of this data contract; see the class docstring and serialization methods.
heterogeneityCellDistributionfield(default_factory=CellDistribution)Stored member of this data contract; see the class docstring and serialization methods.

BackgroundChannel.json

View source

def BackgroundChannel.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'private': self.private.json(), 'shared': self.shared.json(), 'heterogeneity': self.heterogeneity.json()}
Implementation
def json(self) -> dict[str, Any]:
        return {
            "private": self.private.json(),
            "shared": self.shared.json(),
            "heterogeneity": self.heterogeneity.json(),
        }
Complete class implementation
class BackgroundChannel:
    private: ShotNoise
    shared: GlobalShotNoise | GroupedShotNoise
    heterogeneity: CellDistribution = field(default_factory=CellDistribution)

    def json(self) -> dict[str, Any]:
        return {
            "private": self.private.json(),
            "shared": self.shared.json(),
            "heterogeneity": self.heterogeneity.json(),
        }

ConductanceBackground

View source

Bind separate excitatory and inhibitory background channels to a named population.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

ConductanceBackground(target: Population | str, excitatory: BackgroundChannel, inhibitory: BackgroundChannel)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
targetPopulation | strrequiredStored member of this data contract; see the class docstring and serialization methods.
excitatoryBackgroundChannelrequiredStored member of this data contract; see the class docstring and serialization methods.
inhibitoryBackgroundChannelrequiredStored member of this data contract; see the class docstring and serialization methods.

ConductanceBackground.json

View source

def ConductanceBackground.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'target': _id(self.target), 'excitatory': self.excitatory.json(), 'inhibitory': self.inhibitory.json()}
Implementation
def json(self) -> dict[str, Any]:
        return {
            "target": _id(self.target),
            "excitatory": self.excitatory.json(),
            "inhibitory": self.inhibitory.json(),
        }
Complete class implementation
class ConductanceBackground:
    target: Population | str
    excitatory: BackgroundChannel
    inhibitory: BackgroundChannel

    def json(self) -> dict[str, Any]:
        return {
            "target": _id(self.target),
            "excitatory": self.excitatory.json(),
            "inhibitory": self.inhibitory.json(),
        }

StructuredPoisson

View source

Declare a named spike input and its homogeneous rate in Hz.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

StructuredPoisson(input: Signal | str, rate_hz: float)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
inputSignal | strrequiredStored member of this data contract; see the class docstring and serialization methods.
rate_hzfloatrequiredRate in spikes per second.

StructuredPoisson.json

View source

def StructuredPoisson.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'kind': 'structured_poisson', 'input': _id(self.input), 'rate_hz': float(self.rate_hz)}
Implementation
def json(self) -> dict[str, Any]:
        return {
            "kind": "structured_poisson",
            "input": _id(self.input),
            "rate_hz": float(self.rate_hz),
        }
Complete class implementation
class StructuredPoisson:
    input: Signal | str
    rate_hz: float

    def json(self) -> dict[str, Any]:
        return {
            "kind": "structured_poisson",
            "input": _id(self.input),
            "rate_hz": float(self.rate_hz),
        }

CorrelatedPoissonAfferents

View source

Source docstring:

Excitatory afferents with shared and population-private components.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

CorrelatedPoissonAfferents(input_e: Signal | str, input_i: Signal | str, shared_rate_hz: float, e_private_rate_hz: float, i_private_rate_hz: float)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
input_eSignal | strrequiredStored member of this data contract; see the class docstring and serialization methods.
input_iSignal | strrequiredStored member of this data contract; see the class docstring and serialization methods.
shared_rate_hzfloatrequiredStored member of this data contract; see the class docstring and serialization methods.
e_private_rate_hzfloatrequiredStored member of this data contract; see the class docstring and serialization methods.
i_private_rate_hzfloatrequiredStored member of this data contract; see the class docstring and serialization methods.

CorrelatedPoissonAfferents.json

View source

def CorrelatedPoissonAfferents.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'kind': 'correlated_poisson_afferents', 'input_e': _id(self.input_e), 'input_i': _id(self.input_i), 'shared_rate_hz': float(self.shared_rate_hz), 'e_private_rate_hz': float(self.e_private_rate_hz), 'i_private_rate_hz': float(self.i_private_rate_hz)}
Implementation
def json(self) -> dict[str, Any]:
        return {
            "kind": "correlated_poisson_afferents",
            "input_e": _id(self.input_e),
            "input_i": _id(self.input_i),
            "shared_rate_hz": float(self.shared_rate_hz),
            "e_private_rate_hz": float(self.e_private_rate_hz),
            "i_private_rate_hz": float(self.i_private_rate_hz),
        }
Complete class implementation
class CorrelatedPoissonAfferents:
    """Excitatory afferents with shared and population-private components."""

    input_e: Signal | str
    input_i: Signal | str
    shared_rate_hz: float
    e_private_rate_hz: float
    i_private_rate_hz: float

    def json(self) -> dict[str, Any]:
        return {
            "kind": "correlated_poisson_afferents",
            "input_e": _id(self.input_e),
            "input_i": _id(self.input_i),
            "shared_rate_hz": float(self.shared_rate_hz),
            "e_private_rate_hz": float(self.e_private_rate_hz),
            "i_private_rate_hz": float(self.i_private_rate_hz),
        }

StationaryRateWeather

View source

Source docstring:

Positive stationary slow modulation shared by external input channels.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

StationaryRateWeather(tau_ms: float, std_fraction: float)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
tau_msfloatrequiredDecay time constant in milliseconds.
std_fractionfloatrequiredStored member of this data contract; see the class docstring and serialization methods.

StationaryRateWeather.json

View source

def StationaryRateWeather.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'kind': 'stationary_lognormal', 'tau_ms': float(self.tau_ms), 'std_fraction': float(self.std_fraction)}
Implementation
def json(self) -> dict[str, Any]:
        return {
            "kind": "stationary_lognormal",
            "tau_ms": float(self.tau_ms),
            "std_fraction": float(self.std_fraction),
        }
Complete class implementation
class StationaryRateWeather:
    """Positive stationary slow modulation shared by external input channels."""

    tau_ms: float
    std_fraction: float

    def json(self) -> dict[str, Any]:
        return {
            "kind": "stationary_lognormal",
            "tau_ms": float(self.tau_ms),
            "std_fraction": float(self.std_fraction),
        }

TransientAfferentWave

View source

Source docstring:

Smooth finite rate multiplier applied to explicit afferent sources.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

TransientAfferentWave(onset_ms: float, peak_ms: float, offset_ms: float, peak_scale: float, shared_peak_scale: float | None = None, plateau_end_ms: float | None = None)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
onset_msfloatrequiredStored member of this data contract; see the class docstring and serialization methods.
peak_msfloatrequiredStored member of this data contract; see the class docstring and serialization methods.
offset_msfloatrequiredStored member of this data contract; see the class docstring and serialization methods.
peak_scalefloatrequiredStored member of this data contract; see the class docstring and serialization methods.
shared_peak_scalefloat | NoneNoneStored member of this data contract; see the class docstring and serialization methods.
plateau_end_msfloat | NoneNoneStored member of this data contract; see the class docstring and serialization methods.

TransientAfferentWave.json

View source

def TransientAfferentWave.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'kind': 'smooth_transient', 'onset_ms': float(self.onset_ms), 'peak_ms': float(self.peak_ms), 'plateau_end_ms': float(self.peak_ms if self.plateau_end_ms is None else self.plateau_end_ms), 'offset_ms': float(self.offset_ms), 'baseline_scale': 1.0, 'peak_scale': float(self.peak_scale), 'shared_peak_scale': float(self.peak_scale if self.shared_peak_scale is None else self.shared_peak_scale)}
Implementation
def json(self) -> dict[str, Any]:
        return {
            "kind": "smooth_transient",
            "onset_ms": float(self.onset_ms),
            "peak_ms": float(self.peak_ms),
            "plateau_end_ms": float(
                self.peak_ms if self.plateau_end_ms is None else self.plateau_end_ms
            ),
            "offset_ms": float(self.offset_ms),
            "baseline_scale": 1.0,
            "peak_scale": float(self.peak_scale),
            "shared_peak_scale": float(
                self.peak_scale
                if self.shared_peak_scale is None
                else self.shared_peak_scale
            ),
        }
Complete class implementation
class TransientAfferentWave:
    """Smooth finite rate multiplier applied to explicit afferent sources."""

    onset_ms: float
    peak_ms: float
    offset_ms: float
    peak_scale: float
    shared_peak_scale: float | None = None
    plateau_end_ms: float | None = None

    def json(self) -> dict[str, Any]:
        return {
            "kind": "smooth_transient",
            "onset_ms": float(self.onset_ms),
            "peak_ms": float(self.peak_ms),
            "plateau_end_ms": float(
                self.peak_ms if self.plateau_end_ms is None else self.plateau_end_ms
            ),
            "offset_ms": float(self.offset_ms),
            "baseline_scale": 1.0,
            "peak_scale": float(self.peak_scale),
            "shared_peak_scale": float(
                self.peak_scale
                if self.shared_peak_scale is None
                else self.shared_peak_scale
            ),
        }

ConductanceSchedule

View source

Declare a time-varying multiplier for explicit target populations between start_ms and end_ms. shape defaults to smoothstep; endpoint scales default to one.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

ConductanceSchedule(targets: Sequence[Population | str], start_ms: float, end_ms: float, start_scale: float = 1.0, end_scale: float = 1.0, shape: str = 'smoothstep')

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
targetsSequence[Population | str]requiredNamed integer targets or target objects, according to this contract.
start_msfloatrequiredStored member of this data contract; see the class docstring and serialization methods.
end_msfloatrequiredStored member of this data contract; see the class docstring and serialization methods.
start_scalefloat1.0Stored member of this data contract; see the class docstring and serialization methods.
end_scalefloat1.0Stored member of this data contract; see the class docstring and serialization methods.
shapestr'smoothstep'Explicit dimensions/axes or layout shape, as required by the containing contract.

ConductanceSchedule.json

View source

def ConductanceSchedule.json(self) -> dict[str, Any]

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'kind': 'conductance_schedule', 'targets': [_id(target) for target in self.targets], 'shape': self.shape, 'start_ms': float(self.start_ms), 'end_ms': float(self.end_ms), 'start_scale': float(self.start_scale), 'end_scale': float(self.end_scale)}
Implementation
def json(self) -> dict[str, Any]:
        return {
            "kind": "conductance_schedule",
            "targets": [_id(target) for target in self.targets],
            "shape": self.shape,
            "start_ms": float(self.start_ms),
            "end_ms": float(self.end_ms),
            "start_scale": float(self.start_scale),
            "end_scale": float(self.end_scale),
        }
Complete class implementation
class ConductanceSchedule:
    targets: Sequence[Population | str]
    start_ms: float
    end_ms: float
    start_scale: float = 1.0
    end_scale: float = 1.0
    shape: str = "smoothstep"

    def json(self) -> dict[str, Any]:
        return {
            "kind": "conductance_schedule",
            "targets": [_id(target) for target in self.targets],
            "shape": self.shape,
            "start_ms": float(self.start_ms),
            "end_ms": float(self.end_ms),
            "start_scale": float(self.start_scale),
            "end_scale": float(self.end_scale),
        }

SimulationSpec

View source

Collect spike sources, conductance backgrounds, schedules and optional weather/wave declarations for compilation.

Class decorators: dataclass(frozen=True).

Dataclass constructor parameters. Factory defaults are shown as field declarations; omit these arguments to create fresh values per instance:

SimulationSpec(spike_sources: Sequence[StructuredPoisson | CorrelatedPoissonAfferents] = (), backgrounds: Sequence[ConductanceBackground] = (), modulation: Sequence[ConductanceSchedule] = (), weather: StationaryRateWeather | None = None, afferent_wave: TransientAfferentWave | None = None)

Declared fields, including fields inherited from local data classes:

FieldAnnotationDefaultMeaning
spike_sourcesSequence[StructuredPoisson | CorrelatedPoissonAfferents]()Stored member of this data contract; see the class docstring and serialization methods.
backgroundsSequence[ConductanceBackground]()Stored member of this data contract; see the class docstring and serialization methods.
modulationSequence[ConductanceSchedule]()Stored member of this data contract; see the class docstring and serialization methods.
weatherStationaryRateWeather | NoneNoneStored member of this data contract; see the class docstring and serialization methods.
afferent_waveTransientAfferentWave | NoneNoneStored member of this data contract; see the class docstring and serialization methods.
Complete class implementation
class SimulationSpec:
    spike_sources: Sequence[StructuredPoisson | CorrelatedPoissonAfferents] = ()
    backgrounds: Sequence[ConductanceBackground] = ()
    modulation: Sequence[ConductanceSchedule] = ()
    weather: StationaryRateWeather | None = None
    afferent_wave: TransientAfferentWave | None = None

simulation_dict

View source

def simulation_dict(spec: SimulationSpec, graph_digest: str) -> dict[str, Any]

Serialize a SimulationSpec into snnlang.simulation/v1 and bind it to the supplied graph digest.

ParameterAnnotationDefaultMeaning
specSimulationSpecrequiredDefined by the source contract and implementation below.
graph_digeststrrequiredDefined by the source contract and implementation below.

Return annotation: dict[str, Any].

Return expressions (branch-dependent; names refer to the linked implementation):

{'schema': 'snnlang.simulation/v1', 'graph_digest': graph_digest, 'seed_derivation': 'request_seed+stable_channel_index', 'spike_sources': [source.json() for source in spec.spike_sources], 'backgrounds': [background.json() for background in spec.backgrounds], 'modulation': [schedule.json() for schedule in spec.modulation], 'weather': spec.weather.json() if spec.weather is not None else None, 'afferent_wave': spec.afferent_wave.json() if spec.afferent_wave is not None else None}
Implementation
def simulation_dict(spec: SimulationSpec, graph_digest: str) -> dict[str, Any]:
    return {
        "schema": "snnlang.simulation/v1",
        "graph_digest": graph_digest,
        "seed_derivation": "request_seed+stable_channel_index",
        "spike_sources": [source.json() for source in spec.spike_sources],
        "backgrounds": [background.json() for background in spec.backgrounds],
        "modulation": [schedule.json() for schedule in spec.modulation],
        "weather": spec.weather.json() if spec.weather is not None else None,
        "afferent_wave": (
            spec.afferent_wave.json() if spec.afferent_wave is not None else None
        ),
    }

validate_simulation

View source

def validate_simulation(graph: dict[str, Any], recipe: dict[str, Any]) -> None

Validate a serialized simulation recipe against its graph. Invalid schemas, targets, rates and timing fail with ValueError.

ParameterAnnotationDefaultMeaning
graphdict[str, Any]requiredSerialized graph mapping.
recipedict[str, Any]requiredDefined by the source contract and implementation below.

Return annotation: None.

Explicit exceptions in this implementation; called helpers may raise additional errors:

Explicit exception expression
ValueError('unsupported simulation schema')
ValueError('simulation may declare only one background per population')
ValueError('stationary weather requires tau_ms > 0 and std_fraction >= 0')
ValueError('afferent wave requires 0 <= onset < peak <= plateau_end < offset and peak scales >= 1')
ValueError('simulation spike source must reference an input and use rate_hz >= 0')
ValueError('correlated afferents require distinct E/I inputs and non-negative rates')
ValueError(f'unsupported simulation spike source: {kind!r}')
ValueError('conductance background must target a spiking population')
ValueError('conductance schedule requires smoothstep and 0 <= start_ms < end_ms')
ValueError('conductance schedule references an unknown population')
ValueError(f'{polarity} background requires one valid {ownership} stream')
ValueError('background rates/amplitudes must be non-negative and tau_ms positive')
ValueError('grouped shot noise requires group_size > 0')
Implementation
def validate_simulation(graph: dict[str, Any], recipe: dict[str, Any]) -> None:
    if recipe.get("schema") != "snnlang.simulation/v1":
        raise ValueError("unsupported simulation schema")
    input_ids = {row["id"] + ".value" for row in graph.get("inputs", [])}
    population_ids = {
        row["id"] for row in graph.get("populations", []) if row.get("spiking")
    }
    for source in recipe.get("spike_sources", []):
        kind = source.get("kind")
        if kind == "structured_poisson" and (
            source.get("input") not in input_ids or float(source.get("rate_hz", 0)) < 0
        ):
            raise ValueError(
                "simulation spike source must reference an input and use rate_hz >= 0"
            )
        if kind == "correlated_poisson_afferents" and (
            source.get("input_e") not in input_ids
            or source.get("input_i") not in input_ids
            or source.get("input_e") == source.get("input_i")
            or any(
                float(source.get(key, -1)) < 0
                for key in (
                    "shared_rate_hz",
                    "e_private_rate_hz",
                    "i_private_rate_hz",
                )
            )
        ):
            raise ValueError(
                "correlated afferents require distinct E/I inputs and non-negative rates"
            )
        if kind not in {"structured_poisson", "correlated_poisson_afferents"}:
            raise ValueError(f"unsupported simulation spike source: {kind!r}")
    targets = []
    for background in recipe.get("backgrounds", []):
        target = background.get("target")
        targets.append(target)
        if target not in population_ids:
            raise ValueError("conductance background must target a spiking population")
        for polarity in ("excitatory", "inhibitory"):
            channel = background.get(polarity, {})
            for ownership, expected in (
                ("private", {"shot_noise"}),
                ("shared", {"global_shot_noise", "grouped_shot_noise"}),
            ):
                noise = channel.get(ownership, {})
                if noise.get("kind") not in expected:
                    raise ValueError(
                        f"{polarity} background requires one valid {ownership} stream"
                    )
                if (
                    any(
                        float(noise.get(key, 0)) < 0 for key in ("rate_hz", "amplitude")
                    )
                    or float(noise.get("tau_ms", 0)) <= 0
                ):
                    raise ValueError(
                        "background rates/amplitudes must be non-negative and tau_ms positive"
                    )
                if (
                    noise.get("kind") == "grouped_shot_noise"
                    and int(noise.get("group_size", 0)) <= 0
                ):
                    raise ValueError("grouped shot noise requires group_size > 0")
    if len(targets) != len(set(targets)):
        raise ValueError("simulation may declare only one background per population")
    for schedule in recipe.get("modulation", []):
        if (
            schedule.get("shape") != "smoothstep"
            or float(schedule.get("start_ms", -1)) < 0
            or float(schedule.get("end_ms", 0)) <= float(schedule.get("start_ms", -1))
        ):
            raise ValueError(
                "conductance schedule requires smoothstep and 0 <= start_ms < end_ms"
            )
        if not set(schedule.get("targets", ())) <= population_ids:
            raise ValueError("conductance schedule references an unknown population")
    weather = recipe.get("weather")
    if weather is not None and (
        weather.get("kind") != "stationary_lognormal"
        or float(weather.get("tau_ms", 0)) <= 0
        or float(weather.get("std_fraction", -1)) < 0
    ):
        raise ValueError("stationary weather requires tau_ms > 0 and std_fraction >= 0")
    wave = recipe.get("afferent_wave")
    if wave is not None and (
        wave.get("kind") != "smooth_transient"
        or float(wave.get("onset_ms", -1)) < 0
        or not float(wave.get("onset_ms", -1))
        < float(wave.get("peak_ms", -1))
        <= float(wave.get("plateau_end_ms", wave.get("peak_ms", -1)))
        < float(wave.get("offset_ms", -1))
        or float(wave.get("baseline_scale", 0)) != 1.0
        or float(wave.get("peak_scale", 0)) < 1.0
        or float(wave.get("shared_peak_scale", wave.get("peak_scale", 0))) < 1.0
    ):
        raise ValueError(
            "afferent wave requires 0 <= onset < peak <= plateau_end < offset "
            "and peak scales >= 1"
        )

On this page