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.
getdictionary,getphonemes,getduration,generate_lexiconand the shortcutget_lexiconbuild lexicons.generate_sequencebuilds the timed sequence from a generator function;word_phonemes_sequenceis the generator provided (modes:fixed,:random,:balanced).generate_balanced_sequenceshuffles a balanced list of sounds.sign_intervals,all_intervals,merge_intervals,time_in_interval,start_interval,sequence_endquery the timing.stimuli_names(w_/p_prefixed names) andsymbol_namesreturn the symbols;getstimsym,getstim,getneuronslook up stimuli.
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 msStimuli 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
SNNUtilsProtocols and analysis tools built on SNNModels:
- word/phoneme sequences and the Poisson stimuli that present them (
get_lexicon,generate_sequence,word_phonemes_sequence,step_input,update_stimuli!, ...); - excitation/inhibition balance of dendritic neurons (
compute_kei); - import of BioSeq tasks (
import_bioseq_tasks,seq_bioseq); - short-term-plasticity parameters sampled from experimental fits (
sample_stp_params,sample_stp_campagnola); - weight and decoding analysis (
average_weight_dynamics,SVCtrain,score_spikes,trial_average, ...).
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 whosestimfield contains the stimuli.seq: lexicon or sequence withsymbols.wordsandsymbols.phonemes.words::Bool = true,phonemes::Bool = true: activation state to set.
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 apopfield.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 innetwork.pop.p_post::Real: probability that a neuron ofpopreceives 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)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
seq: sequence created bygenerate_sequence.model: model whosestimfield contains the stimuli (e.g. created withstep_input).
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.
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.
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 aDict{Symbol,Float32}that also contains the silence symbol:_(seegetduration).
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))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):
words: word symbol of each element (:_for silence);phonemes: phoneme symbol;duration: duration in ms,lexicon.ph_duration[phoneme];type::onsetfor the first phoneme of a word,:offsetfor the last one,:offset_jfor the intermediate ones of longer words (counted backwards from the end),:silence;onset: start time (ms) of the element;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, :]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])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])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.
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).
SNNUtils.getphonemes — Method
getphonemes(dictionary::Dict{Symbol,Vector{Symbol}})Return the unique phonemes occurring in dictionary, followed by the silence symbol :_.
SNNUtils.getstim — Method
getstim(stim, word, target)Return the field getstimsym(word, target) of the stimulus collection stim.
SNNUtils.getstimsym — Method
getstimsym(word, target)Return the stimulus name Symbol(word, "_", target), or Symbol(word) when target is nothing or :s (soma).
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]]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.
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)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.
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 = ...)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.)
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.
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 wordwinweights(aDictword => weight) is presentedfloor(Int, weights[w] * presentations / sum(values(weights)))times, plus one extra presentation for the words with the largest remainders until the total ispresentations, in random order.:random: words are sampled independently with probabilities proportional toweights[w](0 for words not inweights).:balanced: words are sampled with weightsexp(-count(w)), favouring words presented less often;weightsis 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)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).
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 byresidual_currentandcompute_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.
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.
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.
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.
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).
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.
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).
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.
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 = :_.
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.)
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).
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).
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.)
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.)
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.
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.
Weights and decoding analysis
average_weight_dynamics: mean weight between two neuron groups over a weight record.spikecount_features,sym_features: feature matrices (neurons x windows) from spike counts or recorded variables.SVCtrain(linear SVM) andSNNUtils.LogRegtrain(multinomial logistic regression): train/test a decoder, return Cohen's kappa and the confusion matrix.MultinomialLogisticRegression: random train/test split, returns the test accuracy.score_spikes: decode the presented word from the activity of the word assemblies.trial_average,trial_sort,symbols_to_int,standardize,do_pca: helpers.
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 labelSNNUtils.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.
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).
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).
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.)
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)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.
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 withpopandstim;seq: sequence fromgenerate_sequence.target_interval = :offset: element type used as decoding time.delay = nothing: delay (ms) of the decoding window; ifnothing, the delays-100:10:100are all tested.pop = :Exc: population to decode from.
Returns
- With
delay:(score, confusion_matrix, activity_matrix), wherescoreis Cohen's kappa andactivity_matrix[i, j]the mean count of assemblyiduring wordj, averaged over the presentations ofj. - 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.)
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]])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).
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.)
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))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)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.