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 typeFieldDefaultMeaning
PoissonHomoParameterrate1Hzsame rate for all neurons
PoissonHetParameterrate1Hz (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}\]

FieldDefaultUnitsMeaning
β0-gain of the noise
τ50msmsnoise filter time constant
r01kHz1/mstarget 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 a Float64 population (see float_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)
source
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}).

source
SNNModels.PoissonParameter — Type
PoissonParameter <: AbstractPopulationParameter
PoissonParameter(rate::Real; FT = Float32)                   -> PoissonHomoParameter
PoissonParameter(rate::AbstractVector{<:AbstractFloat}; FT = Float32) -> PoissonHetParameter

Abstract 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))).

source
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} with param = VariablePoissonParameter(FT = Float64) gives a Float64 population.
  • spikes::SpikeIndex, randcache, records::Dict. The spikes are sampled as in Poisson.

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)
source
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.
source

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}\]

FieldDefaultUnitsMeaning
β0-gain of the noise
τ50msmsnoise filter time constant
r01kHz1/mstarget rate of each neuron
rate_timescale400msmstime 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} with param = InhomogeneousPoissonParam(FT = Float64) gives a Float64 population (see float_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 by integrate! (see spike_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)
source
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 at r0.
source

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 a Float64 population (see float_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)
source
SNNModels.IdentityParam — Type
IdentityParam <: AbstractPopulationParameter

Parameter type of Identity (no fields). Population(IdentityParam(); kwargs...) returns an Identity.

source
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.

source