SNNUtils: protocols and analysis

Stimulation protocols and analysis tools of SNNUtils.jl; load them with using SNNUtils.

Word and phoneme sequences

A lexicon maps words to phonemes; a sequence is the timed list of their presentations.

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
lexicon = get_lexicon([:AB, :BA, :CC], 50ms)          # every phoneme lasts 50 ms
seq = generate_sequence(word_phonemes_sequence; lexicon, presentations = 6, mode = :fixed,
                        weights = Dict(:AB => 1.0, :BA => 1.0, :CC => 1.0), seed = 1)
seq.sequence[seq.line_id.type, :]                      # :silence, :onset, :offset, ...
sign_intervals(:AB, seq)                               # [start, end] intervals in ms
sequence_end(seq)                                      # total duration in ms

Stimuli for sequences

step_input makes one Poisson stimulus group per symbol, update_stimuli! copies the sequence intervals into them, set_stimuli! switches words or phonemes on and off.

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
Exc = SNN.Tripod(N = 100, name = "Exc")
lexicon = get_lexicon([:AB, :BA], 50ms)
stim = step_input(; inputs = stimuli_names(lexicon).all, network = SNN.compose(; Exc),
                  pop = :Exc, targets = [:d1, :d2], p_post = 0.1, peak_rate = 8Hz,
                  proj_strength = 2.0)
seq = generate_sequence(word_phonemes_sequence; lexicon, presentations = 4, mode = :fixed,
                        weights = Dict(:AB => 1.0, :BA => 1.0))
model = SNN.compose(; Exc, stim)
update_stimuli!(; seq, model)                 # stimulus intervals from the sequence
set_stimuli!(; model, seq, phonemes = false)  # present words only
SNN.sim!(; model, duration = 200ms)
SNNUtils.SNNUtils — Module
SNNUtils

Protocols and analysis tools built on SNNModels:

source
SNNUtils.set_stimuli! — Method
set_stimuli!(; model, seq, words = true, phonemes = true)

Activate or deactivate the word and phoneme stimuli of model.

For every word w in seq.symbols.words the stimulus model.stim[:w_<w>] (if present) is set active with set_active!(stim, words); likewise model.stim[:p_<ph>] for every phoneme with phonemes. Stimuli that are not in model.stim are skipped. The names follow stimuli_names. Returns nothing; the model is modified in place.

Arguments

  • model: model whose stim field contains the stimuli.
  • seq: lexicon or sequence with symbols.words and symbols.phonemes.
  • words::Bool = true, phonemes::Bool = true: activation state to set.
source
SNNUtils.step_input — Method
step_input(; inputs, network::NamedTuple, sym::Symbol = :glu,
           targets::Union{Vector{Symbol},Vector{Nothing}} = [nothing], pop::Symbol = :Exc,
           p_post::Real, peak_rate::Real, proj_strength::Real, kwargs...)

Create one Poisson input group per symbol in inputs, each projecting onto population pop of network.

For every symbol s in inputs the function builds a MultiCompartmentStimulusGroup(PoissonInterval(rate = peak_rate, μ = proj_strength), network.pop[pop], sym, targets; p_post, neurons = :p_post, name = "s"): a Poisson source with rate peak_rate that is active only inside its intervals (set them later with update_stimuli! or set_intervals!), connected with strength proj_strength to a random fraction p_post of the neurons of the target population, on the receptor sym of each compartment in targets.

With the default targets = [nothing] the input targets a point neuron (one element per group); pass the compartments for dendritic neurons (e.g. [:d1, :d2] for a Tripod). (Up to SNNUtils 0.2.9, with SNNModels 1.8.4, the default raised a MethodError.)

Arguments

  • inputs: iterable of stimulus names (e.g. stimuli_names(lexicon).all).
  • network::NamedTuple: model with a pop field.
  • sym::Symbol = :glu: target receptor/variable.
  • targets = [nothing]: target compartments ([nothing] for a point neuron, e.g. [:d1, :d2] for a Tripod).
  • pop::Symbol = :Exc: key of the target population in network.pop.
  • p_post::Real: probability that a neuron of pop receives the input.
  • peak_rate::Real: Poisson rate inside the active intervals (library unit: kHz, write e.g. 8Hz).
  • proj_strength::Real: synaptic strength μ of the projection.
  • kwargs...: ignored.

Returns

A NamedTuple mapping Symbol(s) to the corresponding stimulus group.

Example

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
Exc = SNN.Tripod(N = 100, name = "Exc")
network = SNN.compose(; Exc)
lexicon = get_lexicon([:AB, :BA], 50ms)
stim = step_input(; inputs = stimuli_names(lexicon).all, network, pop = :Exc,
                  targets = [:d1, :d2], p_post = 0.1, peak_rate = 8Hz, proj_strength = 2.0)
keys(stim)  # :w_AB, :w_BA, :p_A, :p_B (in some order)
source
SNNUtils.update_stimuli! — Method
update_stimuli!(; seq, model)

Set the active intervals of the word and phoneme stimuli of model from the sequence seq.

For every word w (phoneme ph) of seq.symbols, the intervals returned by sign_intervals(w, seq) are assigned with set_intervals! to model.stim[:w_<w>] (model.stim[:p_<ph>]); stimuli not present in model.stim are skipped. Returns model.

Arguments

source
SNNUtils.all_intervals — Method
all_intervals(sym::Symbol, sequence; interval::Vector = [-50ms, 100ms])

For every symbol of the class sym (:words or :phonemes) and every interval returned by sign_intervals, return the window interval_end .+ interval (ms) around the end of the interval, together with the symbol.

Returns

(offsets, ys): a Vector{Vector{Float32}} of windows and the Vector{Symbol} of the corresponding symbols, grouped by symbol.

source
SNNUtils.generate_balanced_sequence — Method
generate_balanced_sequence(sounds, sequence_length)

Return a shuffled vector of length sequence_length in which every element of sounds appears sequence_length ÷ length(sounds) times, plus the first sequence_length % length(sounds) sounds once more.

source
SNNUtils.generate_lexicon — Method
generate_lexicon(config)

Build a lexicon from a word-to-phoneme dictionary.

Arguments

  • config: any object with the fields (or keys accessible with @unpack)
    • dictionary::Dict{Symbol,Vector{Symbol}}: word => its sequence of phonemes;
    • ph_duration: duration of each phoneme in ms, normally a Dict{Symbol,Float32} that also contains the silence symbol :_ (see getduration).

Returns

A NamedTuple with fields

  • dict: the input dictionary;
  • symbols = (phonemes, words): sorted vectors of the unique phonemes and words (the silence symbol is not included);
  • ph_duration: the input durations;
  • silence = :_: the silence symbol.

Example

using SNNUtils
dictionary = getdictionary([:AB, :BA])
lexicon = generate_lexicon((ph_duration = getduration(dictionary, 50.0), dictionary = dictionary))
source
SNNUtils.generate_sequence — Method
generate_sequence(seq_function::Function; lexicon::NamedTuple, seed = -1, kwargs...)

Generate a timed sequence of words and phonemes.

seq_function(; lexicon, kwargs...) must return (words, phonemes, seq_length): two vectors of equal length with the word and the phoneme of every sequence element, and their length (word_phonemes_sequence is the generator provided by SNNUtils). If seed > 0 the global RNG is seeded with Random.seed!(seed) first.

The sequence is stored as a 6 x (seq_length + 2) Matrix{Any}, with a silence element added at the beginning and at the end. Rows (see line_id):

  1. words: word symbol of each element (:_ for silence);
  2. phonemes: phoneme symbol;
  3. duration: duration in ms, lexicon.ph_duration[phoneme];
  4. type: :onset for the first phoneme of a word, :offset for the last one, :offset_j for the intermediate ones of longer words (counted backwards from the end), :silence;
  5. onset: start time (ms) of the element;
  6. offset: end time (ms) of the element.

Returns

The lexicon NamedTuple extended with sequence (the matrix above) and line_id = (words = 1, phonemes = 2, duration = 3, type = 4, onset = 5, offset = 6).

Example

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
lexicon = get_lexicon([:AB, :BA, :CC], 50ms)
seq = generate_sequence(word_phonemes_sequence; lexicon, presentations = 6, mode = :fixed,
                        weights = Dict(:AB => 1.0, :BA => 1.0, :CC => 1.0), seed = 1)
seq.sequence[seq.line_id.words, :]
source
SNNUtils.get_lexicon — Function
get_lexicon(words, duration, insert = nothing)

Convenience constructor of a lexicon: getdictionary(words, insert), then getduration(dictionary, duration), then generate_lexicon.

duration is either a number (the same duration, in ms, for every phoneme and for silence) or a NamedTuple with one entry per phoneme plus silence (see getduration).

Example

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
lexicon = get_lexicon([:AB, :BA, :CC], 50ms)
lexicon.symbols   # (phonemes = [:A, :B, :C], words = [:AB, :BA, :CC])
source
SNNUtils.getdictionary — Method
getdictionary(words::Vector{T}, insert = nothing) where {T<:Union{String,Symbol}}

Create a Dict{Symbol,Vector{Symbol}} mapping each word to the vector of its characters, used as phonemes. If insert is given, Symbol(insert) is placed between consecutive characters (e.g. getdictionary(["ab"], :_) gives :ab => [:a, :_, :b]).

Example

using SNNUtils
getdictionary([:AB, :CD])   # Dict(:AB => [:A, :B], :CD => [:C, :D])
source
SNNUtils.getduration — Method
getduration(dictionary::Dict{Symbol,Vector{Symbol}}, duration::Real)
getduration(dictionary::Dict{Symbol,Vector{Symbol}}, duration::NamedTuple)

Return a Dict{Symbol,Float32} with the duration (ms) of every phoneme of dictionary and of the silence symbol :_.

With a number, every phoneme (and silence) gets Float32(duration). With a NamedTuple, each phoneme ph gets duration[ph] and the silence gets duration.silence; an error is thrown if a phoneme is missing.

source
SNNUtils.getneurons — Function
getneurons(stim, symbol, target = nothing)

Return the unique indices of the neurons targeted by the stimulus stim[Symbol(symbol, "_", target)] (or stim[symbol] when target is nothing or :s).

source
SNNUtils.getphonemes — Method
getphonemes(dictionary::Dict{Symbol,Vector{Symbol}})

Return the unique phonemes occurring in dictionary, followed by the silence symbol :_.

source
SNNUtils.getstimsym — Method
getstimsym(word, target)

Return the stimulus name Symbol(word, "_", target), or Symbol(word) when target is nothing or :s (soma).

source
SNNUtils.merge_intervals — Function
merge_intervals(intervals::Vector{Vector{Float32}}, skip = nothing)

Merge consecutive intervals that touch (end of one equal to start of the next) into a single interval. skip is unused.

merge_intervals([[0, 1], [3, 4], [5, 6]]) returns the three intervals. (Up to SNNUtils 0.2.9 the last interval was dropped when it did not touch the previous one.)

Example

using SNNUtils
merge_intervals([[0f0, 1f0], [1f0, 2f0]])   # [[0.0, 2.0]]
source
SNNUtils.sequence_end — Method
sequence_end(seq)

Return the total duration of the sequence in ms, i.e. the sum of the duration row of seq.sequence.

source
SNNUtils.sign_intervals — Method
sign_intervals(sign::Symbol, sequence)

Return the time intervals during which the word or phoneme sign is presented in sequence.

The row of the sequence is selected by looking sign up in sequence.symbols (words or phonemes). Each sequence element equal to sign contributes one interval [t_start, t_end] (ms); consecutive elements are not merged, so a two-phoneme word yields two adjacent intervals per presentation (see merge_intervals). Throws an error if sign is neither a word nor a phoneme of the lexicon.

Returns

Vector{Vector{Float32}} of [start, end] intervals in ms.

Example

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
lexicon = get_lexicon([:AB, :BA], 50ms)
seq = generate_sequence(word_phonemes_sequence; lexicon, presentations = 4, mode = :fixed,
                        weights = Dict(:AB => 1.0, :BA => 1.0))
sign_intervals(:AB, seq)
source
SNNUtils.start_interval — Method
start_interval(x::Float32, intervals::Vector{Vector{Float32}})

Return the start of the first interval that contains x (bounds included), or -1 if x is in none of the intervals.

source
SNNUtils.stimuli_names — Method
stimuli_names(lexicon)

Return the stimulus names used by step_input, set_stimuli! and update_stimuli!: words prefixed with w_, phonemes prefixed with p_.

Returns

(words, phonemes, all), where all = vcat(words, phonemes).

Example

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
stimuli_names(get_lexicon([:AB], 50ms))  # (words = [:w_AB], phonemes = [:p_A, :p_B], all = ...)
source
SNNUtils.symbol_names — Method
symbol_names(seq)

Return the phoneme and word symbols of a lexicon or sequence, without prefixes, as (phonemes, words, all) with all = vcat(words, phonemes). (Use stimuli_names for the w_/p_-prefixed stimulus names.)

source
SNNUtils.time_in_interval — Method
time_in_interval(x::Float32, intervals::Vector{Vector{Float32}})

Return true if interval[1] <= x <= interval[2] for at least one interval (bounds included), false otherwise.

source
SNNUtils.word_phonemes_sequence — Method
word_phonemes_sequence(; lexicon, weights = nothing, mode = :fixed, seed = nothing,
                       silent_intervals = 1, presentations, kwargs...)

Sequence generator for generate_sequence: draw presentations words from lexicon.dict and expand each into its phonemes, followed by silent_intervals silence elements.

Modes:

  • :fixed: every word w in weights (a Dict word => weight) is presented floor(Int, weights[w] * presentations / sum(values(weights))) times, plus one extra presentation for the words with the largest remainders until the total is presentations, in random order.
  • :random: words are sampled independently with probabilities proportional to weights[w] (0 for words not in weights).
  • :balanced: words are sampled with weights exp(-count(w)), favouring words presented less often; weights is ignored.

weights = nothing means equal weights for all the words of the lexicon. If seed !== nothing, Random.seed!(seed) is called. One final silence element is appended. The word counts are logged at debug level.

(Up to SNNUtils 0.2.9 :fixed failed with pop! on an empty list when the rounded counts summed to less than presentations, weights = nothing failed for :fixed/:random, and the function printed with @show.)

Returns

(words, phonemes, seq_length): the word of each element, the phoneme of each element, and their common length.

Example

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
lexicon = get_lexicon([:AB, :BA], 50ms)
words, phonemes, L = word_phonemes_sequence(; lexicon, presentations = 4, mode = :balanced)
source

Excitation/inhibition balance of dendritic neurons

compute_kei, residual_current, optimal_kei: inhibitory/excitatory rate ratio that cancels the dendritic current; get_model: passive properties of the neuron.

SNNUtils.compute_kei — Method
compute_kei(L, rate; NAR = 1.8, Nd = 2, Vs = -55mV)

Inhibitory-to-excitatory rate ratio $k_{EI}$ that makes the net dendritic current zero when the soma of the neuron built by get_model is held at Vs and every dendrite receives excitatory input at rate rate.

With $V_d = (g_L + a)(V_s - V_r)/(N_d g_{ax}) + V_s$, $I_{comp} = -g_{ax}(V_d - V_s) - g_m (V_d - V_r)$ and the receptor currents $I_r$ defined as in residual_current (rate rate for all receptors), it returns

\[k_{EI} = \max\left(0,\; -\frac{\sum_{exc} I_r + I_{comp}}{\sum_{inh} I_r}\right).\]

Arguments

  • L: dendritic length; rate: excitatory input rate (kHz in library units).
  • NAR = 1.8: NMDA/AMPA ratio; Nd = 2: number of dendrites; Vs = -55mV: somatic potential.

The neuron is built by get_model (up to SNNUtils 0.2.9 this function always threw, see there).

source
SNNUtils.get_model — Method
get_model(L, NAR, Nd; Vs = -55)

Passive properties of a dendritic neuron with dendrites of length L (human dendritic physiology, create_dendrite(L)), the fixed AdEx-like somatic parameters of the Tripod model ($C = 281$ pF, $g_L = 40$ nS, $E_L = V_r = -70.6$ mV, $a = 4$ nS, $b = 80.5$ pA, $\tau_w = 144$ ms) and dendritic receptors with NMDA/AMPA ratio NAR (AMPA $g_0 = 0.73(1 + 1.31/0.73 - NAR)$ nS, NMDA $g_0 = 0.73\,NAR$ nS with $\tau_d = 35$ ms, and the MilesGabaDend GABA receptors).

Arguments

  • L: dendritic length (library length unit, e.g. 200um).
  • NAR: NMDA/AMPA ratio.
  • Nd: number of dendrites (returned; used by residual_current and compute_kei).
  • Vs = -55: target somatic potential (mV), returned unchanged.

Returns

A Dict{Symbol,Any} (from @symdict) with gax (axial conductance), gm (dendritic membrane conductance), gl, a, Vs, Vr, C, dend_syn (vector of the four dendritic Receptors, AMPA, NMDA, GABAa, GABAb), Nd.

Changed after SNNUtils 0.2.9

Up to 0.2.9 get_model (and therefore residual_current, compute_kei, optimal_kei) always threw: it used keywords and types removed from SNNModels and a receptor constructor defined only in an unloaded file. It now computes the same quantities from create_dendrite and AdExParameter.

source
SNNUtils.nmda_curr — Method
nmda_curr(V)

NMDA magnesium-block factor at membrane potential V (mV), with the EyalNMDA parameters (mg, b, k) of SNNModels:

\[B(V) = \left(1 + \frac{[\mathrm{Mg}]}{b}\, e^{k V}\right)^{-1}\]

Returns a Float32 (dimensionless, between 0 and 1). Not exported.

source
SNNUtils.optimal_kei — Method
optimal_kei(l, NAR, Nd; kwargs...)

Evaluate compute_kei(l, rate; NAR, Nd) on 100 log-spaced rates between 1e-2 and 1e3 (library rate unit, kHz) and return the vector of results. kwargs are ignored.

source
SNNUtils.residual_current — Method
residual_current(; λ, kIE, L, NAR, Nd, currents = false, Vs = -55mV)

Residual current at the dendrite of the neuron built by get_model when the soma is held at Vs, with excitatory input rate λ and inhibitory input rate kIE * λ on each dendrite.

The dendritic potential is set to $V_d = \left(g_L (V_s - V_r) + a (V_s - V_r) + N_d g_{ax} V_s\right) / (N_d g_{ax})$, and with $I_{comp} = g_{ax}(V_s - V_d) - g_m (V_d - V_r)$ (the current needed to hold the dendrite at $V_d$, the same expression as in compute_kei; up to SNNUtils 0.2.9 the leak term had the opposite sign, so residual_current was not zero at the $k_{EI}$ returned by compute_kei) the currents are, for every receptor $r$ of the dendritic synapse (first two receptors excitatory, last two inhibitory), $I_r = -\bar g_r (\tau_{d,r} - \tau_{r,r})\, \lambda_r\, B_r(V_d)\, (V_d - E_r)$, where $B_r$ is nmda_curr for NMDA receptors and 1 otherwise.

All keyword arguments except currents and Vs are required. (Up to SNNUtils 0.2.9 their defaults referred to themselves and raised UndefVarError when omitted.)

Returns

sum(exc) + sum(inh) + I_comp, or (exc, inh, I_comp) if currents = true.

The neuron is built by get_model (up to SNNUtils 0.2.9 this function always threw, see there).

source
SNNUtils.all_windows — Function
all_windows(data, ratio = 0.3; max_b = 50)

Number of significant maxima (above ratio times the highest one) of the kernel density estimate of data on -90:-35 for every bandwidth h = 1:max_b. Returns a vector of length max_b. Not exported.

source
SNNUtils.critical_window — Method
critical_window(data; ratio = 0.1, max_b = 50, v_range = collect(-90:-35))

Bimodality index of the samples data (typically membrane potentials in mV), following the kernel-density test of multimodality of Silverman (1981, J. R. Stat. Soc. B, cited in the source as "Using Kernel Density Estimates to Investigate Multimodality").

For bandwidths h = 1, 3, 5, ..., max_b the density is estimated on v_range with the kernel $exp(-x^2 / h) / h$ (globalKDE); a maximum counts if it is larger than ratio times the highest maximum. Returns the first h at which the estimate is no longer bimodal (larger values mean more separated modes), or max_b. Not exported (SNNUtils.critical_window).

source

BioSeq tasks

Import of artificial-grammar tasks in the BioSeq JSON format and storage of the network activity for external analysis: import_bioseq_tasks, bioseq_epochs, bioseq_lexicon, seq_bioseq, root_path, store_experiment_data, store_target_pops, store_labels, store_activity_data.

SNNUtils.bioseq_epochs — Method
bioseq_epochs(experiment, stage)

Return the list of epochs (the values of experiment.task[stage]) of a BioSeq experiment stage.

source
SNNUtils.bioseq_lexicon — Method
bioseq_lexicon(; experiment, duration::Float32 = 50.0f0, kwargs...)

Build a lexicon (same layout as generate_lexicon) from a BioSeq experiment: the words are the strings of experiment.info["task"]["test_string_set"], the phonemes their characters. ph_duration is the scalar duration (ms) shared by all symbols; silence = :_.

source
SNNUtils.import_bioseq_tasks — Method
import_bioseq_tasks(generator_path, task_path)

Read the BioSeq task definitions (JSON files) and pair each task with its generator description.

Returns a vector of NamedTuples (task, info) with the parsed JSON dictionaries: task read from task_path/<name>.json and info from generator_path/<name>.json, for every JSON file name present in both folders (sorted by name). Files present in only one folder are skipped with a warning. (Up to SNNUtils 0.2.9 the names were listed in one folder and read from the other, and the two lists were paired by position.)

source
SNNUtils.root_path — Method
root_path(path, exp)

Create (if needed) and return the folder path/id-<seed>_seed-<seed_network>_<label> for the experiment exp (fields taken from exp.info).

source
SNNUtils.seq_bioseq — Method
seq_bioseq(; experiment, stage::String, kwargs...)

Build the timed sequence of a BioSeq experiment stage. The epochs of stage are concatenated with one silence element after each (# is replaced by silence), words of the lexicon are located in the phoneme stream, and every element lasts ph_duration.

Returns

The lexicon of bioseq_lexicon extended with sequence (a 3 x L matrix: words, phonemes, durations), line_id = (phonemes = 2, words = 1, duration = 3) and timestamps (the duration of each epoch in ms). kwargs are passed to bioseq_lexicon (e.g. duration).

source
SNNUtils.store_activity_data — Method
store_activity_data(_root::String, stage::String, sequence, model; targets = [:d])

Save the activity of a BioSeq run in _root/stage: phoneme labels (store_labels), spike times of model.pop.E, I1, I2 (spiketimes.h5) and, for every epoch, the somatic membrane potential :v_s of E at the end of each element and one element later (membrane_end/epoch_i.npz, membrane_delay/epoch_i.npz).

(Up to SNNUtils 0.2.9 it called SNN.record, undefined inside SNNUtils, and threw at the membrane traces.)

source
SNNUtils.store_experiment_data — Method
store_experiment_data(path, exp, network, seq)

Save the symbol mapping (mapping.h5), the experiment info (info.h5: seed, label, symbol duration) and the neuron index ranges (spikeinfo.h5) of the populations E, I1, I2 of network into root_path(path, exp), and return that folder.

The ranges follow the order in which store_activity_data concatenates the spikes: E (exc), then I1 (pv), then I2 (sst). (Up to SNNUtils 0.2.9 they were built as E, I2.N neurons labelled sst, I1.N neurons labelled pv: wrong labels if I1.N != I2.N.)

source
SNNUtils.store_labels — Method
store_labels(_root, stim, sequence, targets)

Save to labels.h5 in _root a dictionary mapping the onset time of every phoneme presentation (read from the intervals of the stimulus stim[Symbol(phoneme, "_", targets[1])]) to the phoneme, and return the dictionary.

source
SNNUtils.store_target_pops — Method
store_target_pops(_root, seq, stim, targets)

Save to target_pops.h5 in _root the indices of the neurons targeted by the stimulus of every phoneme and word of seq (union over the compartments targets, read as getfield(getfield(stim, symbol), target).neurons). Returns _root.

source

Weights and decoding analysis

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
E = SNN.Poisson(N = 20, param = SNN.PoissonParameter(10Hz))
SNN.monitor!(E, [:fire])
SNN.sim!([E]; duration = 2s)
windows = [[t, t + 100ms] for t in 0ms:100ms:1900ms]
X = spikecount_features(E, windows)                    # 20 x 20 spike counts
labels = repeat([:a, :b], 10)
X[1:5, labels .== :b] .+= 5                            # make the two classes separable
kappa, cm = SVCtrain(X, labels)
code, ulabels = trial_average(X, labels)               # mean features per label
SNNUtils.average_weight — Method
average_weight(pre_pop_neurons::Vector{Int}, post_pop_neurons::Vector{Int}, synapse::SpikingSynapse)

Mean of the current weights synapse.W of the connections from pre_pop_neurons to post_pop_neurons (iterating over the row-ordered index of the sparse synapse). Not exported.

source
SNNUtils.average_weight_dynamics — Method
average_weight_dynamics(pre::Vector{Int}, post::Vector{Int}, synapse::SpikingSynapse, record::Matrix{R}) where {R<:Real}

Average weight of the connections from the neurons pre to the neurons post of synapse, at every column (time point) of record.

record is a matrix of weights with one row per synapse, in the storage order of synapse.W (compressed sparse column order), and one column per time point, e.g. a weight record of the synapse. For each column, the entries of all synapses with presynaptic neuron in pre and postsynaptic neuron in post are averaged (NaN if there is no such synapse).

Returns

Vector{Float64} of length size(record, 2).

source
SNNUtils.LogRegtrain — Method
LogRegtrain(Xs, ys; seed = 123, p = 0.5, gamma = 0, lambda = 0.5)

Multinomial logistic-regression counterpart of SVCtrain (MLJLinearModels MultinomialClassifier(lambda = lambda, fit_intercept = false), inputs standardised with the training-set statistics). gamma is unused. Returns (score, confusion_matrix) with Cohen's kappa. Not exported (SNNUtils.LogRegtrain).

source
SNNUtils.MultinomialLogisticRegression — Method
MultinomialLogisticRegression(X::Matrix{Float64}, labels::Array{Int64}; λ = 0.5, test_ratio = 0.5)

Multinomial logistic regression (MLJLinearModels MultinomialRegression(λ), no intercept) of labels on the features X ((n_features, n_samples)), with z-scoring based on a random training set (a fraction 1 - test_ratio of the samples, drawn with the global RNG) and NaN replaced by 0. Returns (accuracy, params): the accuracy on the test samples and params of size (n_features, n_classes). X is not modified.

(Up to SNNUtils 0.2.9 it called the undefined make_set_index and always threw; it also z-scored X in place and printed with @show.)

source
SNNUtils.SVCtrain — Method
SVCtrain(Xs, ys; seed = 123, p = 0.5, labels = false)

Train a linear support-vector classifier (LIBSVM, cost = 0.01) on the features Xs and labels ys and evaluate it.

A small Gaussian jitter (standard deviation 1e-5) is added to Xs. If p < 1, the samples are split with a stratified partition (fraction p for training, RNG seed seed), the features are z-scored with the statistics of the training set, and the classifier is evaluated on the test set; if either set has fewer than 2 samples, or if p >= 1, all samples are used for both training and testing. labels is unused.

Arguments

  • Xs: feature matrix of size (n_features, n_samples).
  • ys: vector of labels (any type; converted to strings), one per sample.

Returns

(score, confusion_matrix): score is Cohen's kappa of the test predictions (not raw accuracy) and confusion_matrix the corresponding StatisticalMeasures confusion matrix.

Example

using SNNUtils
Xs = hcat(randn(3, 40), randn(3, 40) .+ 3)
ys = vcat(fill(1, 40), fill(2, 40))
kappa, cm = SVCtrain(Xs, ys)
source
SNNUtils.do_pca — Method
do_pca(data::Matrix)

Standardise data with standardize (dimension 1), fit a PCA with MultivariateStats.fit(PCA, data) (observations are the columns) and return the projected data MultivariateStats.transform(pca, data), a matrix of size (n_components, n_samples). The PCA object itself is not returned.

source
SNNUtils.score_spikes — Function
score_spikes(model, seq, target_interval = :offset; delay = nothing, pop = :Exc)

Decode which word was presented from the spike counts of the word assemblies and score the decoding.

The word assemblies are the neurons targeted by the stimuli of model.stim whose names start with w_ (stimuli whose name contains "noise" are excluded), obtained with subpopulations. For each element of seq of type target_interval (row seq.line_id.type, default :offset), the spikes of model.pop[pop] are counted in 10 ms bins over the window offset_time .+ (0:100) .+ delay (ms), and the predicted word is the assembly with the largest mean count.

Arguments

  • model: model with pop and stim; seq: sequence from generate_sequence.
  • target_interval = :offset: element type used as decoding time.
  • delay = nothing: delay (ms) of the decoding window; if nothing, the delays -100:10:100 are all tested.
  • pop = :Exc: population to decode from.

Returns

  • With delay: (score, confusion_matrix, activity_matrix), where score is Cohen's kappa and activity_matrix[i, j] the mean count of assembly i during word j, averaged over the presentations of j.
  • Without delay: (scores, best_delay, (; cms, delays)).

The computation is serial. activity_matrix is computed for the given delay. (Up to SNNUtils 0.2.9 it accumulated over all the delays tested.)

source
SNNUtils.spikecount_features — Method
spikecount_features(pop::T, offsets::Vector) where {T<:AbstractPopulation}

Spike counts of every neuron of pop in each time window of offsets.

offsets is a vector of windows accepted by spiketimes(pop; interval) (e.g. [t0, t1] in ms or a range). Requires a :fire record. Windows are processed in parallel with Threads.@threads.

Returns

Matrix{Float64} of size (pop.N, length(offsets)).

Example

using SpikingNeuralNetworks, SNNUtils
SNN.@load_units
E = SNN.Poisson(N = 10, param = SNN.PoissonParameter(10Hz))
SNN.monitor!(E, [:fire])
SNN.sim!([E]; duration = 1s)
X = spikecount_features(E, [[0ms, 500ms], [500ms, 1000ms]])
source
SNNUtils.standardize — Function
standardize(data::Matrix, dim = 1)

Z-score data with StatsBase.ZScoreTransform fitted with dims = dim and return the transformed copy (the input is not modified).

source
SNNUtils.sym_features — Method
sym_features(sym::Symbol, pop::T, offsets::Vector) where {T<:AbstractPopulation}

Mean of the recorded variable sym of every neuron of pop in each window offset = [t0, t1] of offsets (sampled every 1 ms from the interpolated record). Windows that end after the last recorded time are skipped (their column stays zero). Returns a Matrix{Float64} of size (pop.N, length(offsets)).

(Up to SNNUtils 0.2.9 it called SNN.record, undefined inside SNNUtils, and always threw.)

source
SNNUtils.symbols_to_int — Method
symbols_to_int(symbols)

Map symbols to integers 1:n following the sorted order of the unique symbols.

Returns

(symbols_int, mapping): the vector of integers and the Dict{Symbol,Int} used.

Example

using SNNUtils
symbols_to_int([:b, :a, :b])   # ([2, 1, 2], Dict(:a => 1, :b => 2))
source
SNNUtils.trial_average — Function
trial_average(array::Array, sequence::Vector, dim::Int = -1)

Average array over the trials that share the same label.

sequence gives the label of each slice of array along dimension dim (default: the last dimension). Returns (spatial_code, labels): labels = sort(unique(sequence)) and spatial_code, a Float32 array with the trial dimension removed and a new last dimension of length length(labels) holding the mean over the trials of each label.

Any dim works. (Up to SNNUtils 0.2.9 the mean was taken along the last dimension of the selected slices, which is wrong when dim is not the last dimension.)

Example

using SNNUtils
code, labels = trial_average(rand(4, 6), [1, 2, 1, 2, 1, 2])   # size(code) == (4, 2)
source
SNNUtils.trial_sort — Function
trial_sort(array::Array, sequence::Vector, dim::Int = -1)

Group the slices of array along dim (default: last dimension) by label. Returns (data, labels): data[label] is a vector of copies of the slices with that label, and labels = sort(unique(sequence)). The dictionary is typed Dict{Symbol, Vector{Array}}, so labels must be convertible to Symbol.

source