Spike sources
Populations that produce spikes for connections (unlike stimuli, which write into a population directly), and the relay population Identity.
Poisson population: Poisson
In each step neuron $i$ fires with probability
\[P(\text{spike}) = \nu_i\, dt ,\]
with $\nu_i$ set by the parameter type (PoissonParameter(rate) picks one):
| Parameter type | Field | Default | Meaning |
|---|---|---|---|
PoissonHomoParameter | rate | 1Hz | same rate for all neurons |
PoissonHetParameter | rate | 1Hz (must be a vector of length N) | one rate per neuron |
using SpikingNeuralNetworks
SNN.@load_units
P = SNN.Poisson(N = 100, param = SNN.PoissonParameter(10Hz))
Ph = SNN.Population(SNN.PoissonParameter(collect(range(1Hz, 50Hz, length = 100))); N = 100)
SNN.monitor!([P, Ph], [:fire])
SNN.sim!([P, Ph]; duration = 1s)Population with a shared fluctuating rate: VariablePoisson
One filtered noise $\eta$ ($\xi_t$ uniform in $[-0.5, 0.5]$) modulates the total rate $\nu$; each neuron fires with probability $\nu\, dt / N$:
\[\begin{aligned} \eta &\leftarrow \eta\,(1 - dt/\tau) + \xi_t\, dt/\tau \\ \nu &= \left[\tfrac{r_0}{2}\, F(\beta \eta) + \rho\right]_+, \qquad F(x) = \begin{cases} x & x > 0 \\ 1 & x \le 0 \end{cases} \\ \rho &\leftarrow \rho + (r_0 - \nu)\, dt / 400\,\text{ms} \end{aligned}\]
| Field | Default | Units | Meaning |
|---|---|---|---|
β | 0 | - | gain of the noise |
τ | 50ms | ms | noise filter time constant |
r0 | 1kHz | 1/ms | target total rate of the population |
using SpikingNeuralNetworks
SNN.@load_units
P = SNN.VariablePoisson(N = 100, param = SNN.VariablePoissonParameter(r0 = 2kHz))
SNN.monitor!(P, [:fire])
SNN.sim!([P]; duration = 500ms)SNNModels.Poisson — Type
Poisson(; N = 100, param = PoissonHomoParameter(), name = "Poisson", kwargs...)
Population(param::PoissonParameter; N, kwargs...)Population of independent Poisson spike generators. It has no input: connections cannot target it, it is used as presynaptic population.
Equations
In each step of length $dt$ neuron $i$ fires with probability
\[P(\text{spike}) = \nu_i\, dt ,\]
the Bernoulli approximation of a Poisson process of rate $\nu_i$ (accurate when $\nu_i dt \ll 1$). $\nu_i$ is param.rate (homogeneous) or param.rate[i] (heterogeneous).
Integration
Spikes are sampled sparsely, with one random draw per spike: the gap to the next firing neuron is drawn from the geometric distribution (homogeneous rate), or candidates drawn at the largest probability are thinned (heterogeneous rates). When the largest per-step probability is at least 3 % (_POP_DENSE_SPIKE_P), every neuron is tested against one uniform draw (randcache) instead. spikes holds the ascending list of the neurons that fired (sparse regime only).
Fields
id::String = randstring(12),name::String = "Poisson".param::PoissonParameter = PoissonHomoParameter()(1 Hz).N::Int32 = 100;randcache::VFT(dense regime only);VFT = Vector{Float64}gives aFloat64population (seefloat_type);fire::Vector{Bool};spikes::SpikeIndex;records::Dict.
References
Poisson model of spike generation (D. Heeger, handout).
Example
using SpikingNeuralNetworks
SNN.@load_units
P = SNN.Poisson(N = 100, param = SNN.PoissonParameter(10Hz))
Ph = SNN.Population(SNN.PoissonParameter(collect(range(1Hz, 50Hz, length = 100))); N = 100)
SNN.monitor!([P, Ph], [:fire])
SNN.sim!([P, Ph]; duration = 1s)
SNN.firing_rate(P; interval = 0:10ms:1s)SNNModels.PoissonHetParameter — Type
PoissonHetParameter{FT = Float32}(; rate = 1Hz)Per-neuron firing rates for a Poisson population; rate must be a vector of length N (construct it with PoissonParameter(rates::Vector), which sets FT = Vector{Float32}).
SNNModels.PoissonHomoParameter — Type
PoissonHomoParameter{FT = Float32}(; rate = 1Hz)Same firing rate rate (Hz units) for all neurons of a Poisson population.
SNNModels.PoissonParameter — Type
PoissonParameter <: AbstractPopulationParameter
PoissonParameter(rate::Real; FT = Float32) -> PoissonHomoParameter
PoissonParameter(rate::AbstractVector{<:AbstractFloat}; FT = Float32) -> PoissonHetParameterAbstract parameter type of the Poisson population, and convenience constructor: a scalar rate gives a homogeneous population, a vector of rates (one per neuron) a heterogeneous one. Rates are in the library unit system (use Hz, e.g. 10Hz). FT is the precision of the rate: FT = Float64 for a Float64 population (Poisson(VFT = Vector{Float64}, param = PoissonParameter(10Hz; FT = Float64))).
SNNModels.VariablePoisson — Type
VariablePoisson(; N = 100, param::VariablePoissonParameter, name = "VariablePoisson")Population of N spike generators sharing one time-varying total rate. param has no default and must be given. Not constructible through Population.
Equations
One scalar noise $\eta$ ($\xi_t$ uniform in $[-0.5, 0.5]$), rate offset $\rho$ and total rate $\nu$:
\[\begin{aligned} \eta &\leftarrow \eta\,(1 - dt/\tau) + \xi_t\, dt/\tau \\ \nu &= \left[\tfrac{r_0}{2}\, F(\beta \eta) + \rho\right]_+, \qquad F(x) = \begin{cases} x & x > 0 \\ 1 & x \le 0 \end{cases} \\ \rho &\leftarrow \rho + (r_0 - \nu)\, dt / 400\,\text{ms} \end{aligned}\]
and each neuron fires with probability $\nu\, dt / N$, so $\nu$ is the rate of the whole population. With $\beta = 0$ the slow feedback on $\rho$ (initialised to $r_0$) drives $\nu$ to $r_0$. Note the discontinuity of $F$ at 0 (values of $\beta\eta$ in $(0, 1)$ reduce the rate below the $\beta\eta \le 0$ value).
Fields
id,param,name = "VariablePoisson",N::Int32 = 100,fire.noise::VFT(length 1): $\eta$;r::VFT(length 1): $\rho$;Erate::VFT(length 1): the total rate $\nu$ of the last step.VFT = Vector{Float64}withparam = VariablePoissonParameter(FT = Float64)gives aFloat64population.spikes::SpikeIndex,randcache,records::Dict. The spikes are sampled as inPoisson.
Example
using SpikingNeuralNetworks
SNN.@load_units
P = SNN.VariablePoisson(N = 100, param = SNN.VariablePoissonParameter(r0 = 2kHz))
SNN.monitor!(P, [:fire])
SNN.sim!([P]; duration = 500ms)SNNModels.VariablePoissonParameter — Type
VariablePoissonParameter{FT = Float32}(; β = 0, τ = 50ms, r0 = 1kHz)Parameters of VariablePoisson (FT = Float64 for a Float64 population).
β::FT = 0.0: gain of the filtered noise on the rate.τ::FT = 50ms: time constant of the noise low-pass filter (ms).r0::FT = 1kHz: target total rate of the population.
Inhomogeneous Poisson population: InhomogeneousPoisson
The rate process of VariablePoisson, independent for every neuron:
\[\begin{aligned} \eta_i &\leftarrow \eta_i\,(1 - dt/\tau) + \xi_{i,t}\, dt/\tau \\ \nu_i &= \left[\tfrac{r_0}{2}\, F(\beta \eta_i) + \rho_i\right]_+ \\ \rho_i &\leftarrow \rho_i + (r_0 - \nu_i)\, dt / \tau_{rate} \\ P(\text{spike}) &= 1 - e^{-\nu_i dt} \end{aligned}\]
| Field | Default | Units | Meaning |
|---|---|---|---|
β | 0 | - | gain of the noise |
τ | 50ms | ms | noise filter time constant |
r0 | 1kHz | 1/ms | target rate of each neuron |
rate_timescale | 400ms | ms | time constant of the rate feedback |
using SpikingNeuralNetworks
SNN.@load_units
P = SNN.Population(SNN.InhomogeneousPoissonParam(r0 = 10Hz, β = 5); N = 100)
SNN.monitor!(P, [:fire])
SNN.sim!([P]; duration = 500ms)SNNModels.InhomogeneousPoisson — Type
InhomogeneousPoisson(; N = 100, param::InhomogeneousPoissonParam, name = "InhomogeneousPoisson")
Population(param::InhomogeneousPoissonParam; N, kwargs...)Population of independent inhomogeneous Poisson generators, each with its own slowly fluctuating rate. param has no default.
Equations
For neuron $i$ ($\xi_{i,t}$ uniform in $[-0.5, 0.5]$):
\[\begin{aligned} \eta_i &\leftarrow \eta_i\,(1 - dt/\tau) + \xi_{i,t}\, dt/\tau \\ \nu_i &= \left[\tfrac{r_0}{2}\, F(\beta \eta_i) + \rho_i\right]_+, \qquad F(x) = \begin{cases} x & x > 0 \\ 1 & x \le 0 \end{cases} \\ \rho_i &\leftarrow \rho_i + (r_0 - \nu_i)\, dt / \tau_{rate} \\ P(\text{spike}) &= 1 - e^{-\nu_i dt} \end{aligned}\]
with $\tau_{rate}$ = rate_timescale; $\rho_i$ (field r) is initialised to $r_0$. The noise uses randcache_β (one uniform number per neuron and step). The spikes are sampled sparsely, as in Poisson.
Fields
id,param,name = "InhomogeneousPoisson",N::Int32 = 100,fire::Vector{Bool}.r::VFT: rate offsets $\rho_i$;noise::VFT: $\eta_i$.VFT = Vector{Float64}withparam = InhomogeneousPoissonParam(FT = Float64)gives aFloat64population (seefloat_type).pspike::VFT: spike probability $1 - e^{-\nu_i dt}$ of each neuron in the last step.spikes::SpikeIndex: neurons that fired in the last step, filled byintegrate!(seespike_index).randcache_β;records::Dict;targets::Dict(unused).
Example
using SpikingNeuralNetworks
SNN.@load_units
P = SNN.Population(SNN.InhomogeneousPoissonParam(r0 = 10Hz, β = 5); N = 100)
SNN.monitor!(P, [:fire])
SNN.sim!([P]; duration = 500ms)SNNModels.InhomogeneousPoissonParam — Type
InhomogeneousPoissonParam{FT = Float32}(; β = 0, τ = 50ms, r0 = 1kHz, rate_timescale = 400ms)Parameters of InhomogeneousPoisson.
β::FT = 0.0: gain of the filtered noise on the rate.τ::FT = 50ms: time constant of the noise low-pass filter (ms).r0::FT = 1kHz: target rate of each neuron.rate_timescale::FT = 400ms: time constant of the feedback that keeps the mean rate atr0.
Relay population: Identity
Neuron $i$ fires in the step after it receives positive input g (any target symbol maps to g).
using SpikingNeuralNetworks
SNN.@load_units
P = SNN.Poisson(N = 10, param = SNN.PoissonParameter(50Hz))
Id = SNN.Identity(N = 10)
s = SNN.SpikingSynapse(P, Id, :g; conn = (μ = 1, p = 1.0))
model = SNN.compose(; P, Id, s)
SNN.monitor!(Id, [:fire])
SNN.sim!(model, 100ms)SNNModels.Identity — Type
Identity(; N = 100, name = "identity", kwargs...)Relay population: each neuron emits a spike in every step in which it received positive input, so that spikes arriving through a connection are relayed to downstream connections. Because populations are integrated before connections are forwarded, input written in step $t$ is turned into a spike in step $t + 1$ (one-step delay). Any target symbol used by a connection is mapped to the input field g.
Integration
For each neuron, per step: h += g; spikecount = g if g > 0 (else 0); fire = g > 0; then g = 0.
Sparse update
If every component that writes g (counted in nwriters) is a stimulus that logs the neurons it writes (SpikeTimeStimulus, the Poisson stimuli; see attach_driver!), integrate! visits only those neurons and the neurons that fired in the previous step, with the same result as the dense update, and feeds the spike list spikes that its connections share. Any other writer, or more writes than neurons in a step, makes the update dense. Do not write g, fire or h of such a population by hand; if a connection was redirected to another target vector after construction, call release_input!.
Fields
name::String = "identity",id::String = randstring(12),param::IdentityParam.N::Int32 = 100.g::VFT: input received in the current step (summed synaptic weights), reset each step.VFT = Vector{Float64}gives aFloat64population (seefloat_type).spikecount::VFT: input of the last step for neurons that fired (0 otherwise).h::VFT: cumulative input since construction.fire::Vector{Bool};records::Dict.spikes::SpikeIndex,inbox::TouchList,nwriters,ndrivers,tracked: state of the sparse update.
Example
using SpikingNeuralNetworks
SNN.@load_units
P = SNN.Poisson(N = 10, param = SNN.PoissonParameter(50Hz))
Id = SNN.Identity(N = 10)
s = SNN.SpikingSynapse(P, Id, :g; conn = (μ = 1, p = 1.0))
model = SNN.compose(; P, Id, s)
SNN.monitor!(Id, [:fire])
SNN.sim!(model, 100ms)SNNModels.IdentityParam — Type
IdentityParam <: AbstractPopulationParameterParameter type of Identity (no fields). Population(IdentityParam(); kwargs...) returns an Identity.
SNNModels.release_input! — Method
release_input!(p::Identity)Declare that one component built with p as target no longer writes to p.g (e.g. the g of a connection was replaced after construction), removing it from the writer count of the sparse update. Calling it for a component that still writes to p.g makes p miss that input.