snnlab.lang.simulation
Complete declared API of the simulation module, with signatures, data fields, validation and source.
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.
| Symbol | Kind |
|---|---|
| ShotNoise | class |
| GlobalShotNoise | class |
| GroupedShotNoise | class |
| CellDistribution | class |
| BackgroundChannel | class |
| ConductanceBackground | class |
| StructuredPoisson | class |
| CorrelatedPoissonAfferents | class |
| StationaryRateWeather | class |
| TransientAfferentWave | class |
| ConductanceSchedule | class |
| SimulationSpec | class |
| simulation_dict | function |
| validate_simulation | function |
ShotNoise
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
rate_hz | float | required | Rate in spikes per second. |
amplitude | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
tau_ms | float | required | Decay time constant in milliseconds. |
ShotNoise.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
rate_hz | float | required | Rate in spikes per second. |
amplitude | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
tau_ms | float | required | Decay time constant in milliseconds. |
GlobalShotNoise.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
rate_hz | float | required | Rate in spikes per second. |
amplitude | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
tau_ms | float | required | Decay time constant in milliseconds. |
group_size | int | 16 | Stored member of this data contract; see the class docstring and serialization methods. |
GroupedShotNoise.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
rate | Spec | field(default_factory=lambda: Spec('constant', {'value': 1.0})) | Stored member of this data contract; see the class docstring and serialization methods. |
amplitude | Spec | field(default_factory=lambda: Spec('constant', {'value': 1.0})) | Stored member of this data contract; see the class docstring and serialization methods. |
CellDistribution.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
private | ShotNoise | required | Stored member of this data contract; see the class docstring and serialization methods. |
shared | GlobalShotNoise | GroupedShotNoise | required | Stored member of this data contract; see the class docstring and serialization methods. |
heterogeneity | CellDistribution | field(default_factory=CellDistribution) | Stored member of this data contract; see the class docstring and serialization methods. |
BackgroundChannel.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
target | Population | str | required | Stored member of this data contract; see the class docstring and serialization methods. |
excitatory | BackgroundChannel | required | Stored member of this data contract; see the class docstring and serialization methods. |
inhibitory | BackgroundChannel | required | Stored member of this data contract; see the class docstring and serialization methods. |
ConductanceBackground.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
input | Signal | str | required | Stored member of this data contract; see the class docstring and serialization methods. |
rate_hz | float | required | Rate in spikes per second. |
StructuredPoisson.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
input_e | Signal | str | required | Stored member of this data contract; see the class docstring and serialization methods. |
input_i | Signal | str | required | Stored member of this data contract; see the class docstring and serialization methods. |
shared_rate_hz | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
e_private_rate_hz | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
i_private_rate_hz | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
CorrelatedPoissonAfferents.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
tau_ms | float | required | Decay time constant in milliseconds. |
std_fraction | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
StationaryRateWeather.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
onset_ms | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
peak_ms | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
offset_ms | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
peak_scale | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
shared_peak_scale | float | None | None | Stored member of this data contract; see the class docstring and serialization methods. |
plateau_end_ms | float | None | None | Stored member of this data contract; see the class docstring and serialization methods. |
TransientAfferentWave.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
targets | Sequence[Population | str] | required | Named integer targets or target objects, according to this contract. |
start_ms | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
end_ms | float | required | Stored member of this data contract; see the class docstring and serialization methods. |
start_scale | float | 1.0 | Stored member of this data contract; see the class docstring and serialization methods. |
end_scale | float | 1.0 | Stored member of this data contract; see the class docstring and serialization methods. |
shape | str | 'smoothstep' | Explicit dimensions/axes or layout shape, as required by the containing contract. |
ConductanceSchedule.json
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
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:
| Field | Annotation | Default | Meaning |
|---|---|---|---|
spike_sources | Sequence[StructuredPoisson | CorrelatedPoissonAfferents] | () | Stored member of this data contract; see the class docstring and serialization methods. |
backgrounds | Sequence[ConductanceBackground] | () | Stored member of this data contract; see the class docstring and serialization methods. |
modulation | Sequence[ConductanceSchedule] | () | Stored member of this data contract; see the class docstring and serialization methods. |
weather | StationaryRateWeather | None | None | Stored member of this data contract; see the class docstring and serialization methods. |
afferent_wave | TransientAfferentWave | None | None | Stored 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 = Nonesimulation_dict
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.
| Parameter | Annotation | Default | Meaning |
|---|---|---|---|
spec | SimulationSpec | required | Defined by the source contract and implementation below. |
graph_digest | str | required | Defined 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
def validate_simulation(graph: dict[str, Any], recipe: dict[str, Any]) -> NoneValidate a serialized simulation recipe against its graph. Invalid schemas, targets, rates and timing fail with ValueError.
| Parameter | Annotation | Default | Meaning |
|---|---|---|---|
graph | dict[str, Any] | required | Serialized graph mapping. |
recipe | dict[str, Any] | required | Defined 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"
)