From 88b0e7cda27321eaedb5ee500e2d8f0bf7769b46 Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Tue, 12 Nov 2024 18:37:08 +1100 Subject: [PATCH 01/43] add my potjans generating sparse matrix --- examples/genPotjansConnectivity.jl | 203 +++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 examples/genPotjansConnectivity.jl diff --git a/examples/genPotjansConnectivity.jl b/examples/genPotjansConnectivity.jl new file mode 100644 index 0000000..7792fea --- /dev/null +++ b/examples/genPotjansConnectivity.jl @@ -0,0 +1,203 @@ +using SparseArrays +using StaticArrays +using ProgressMeter +using LinearAlgebra + +""" +This file contains a function stack that creates a network with Potjans and Diesmann wiring likeness in Julia using SpikingNeuralNetworks.jl to simulate +electrical neural network dynamics. +This code draws heavily on the PyNN OSB Potjans implementation code found here: +https://github.com/OpenSourceBrain/PotjansDiesmann2014/blob/master/PyNN/network_params.py#L139-L146 +However the translation of this Python PyNN code into performant and scalable Julia was not trivial. +Hard coded Potjans parameters follow. +and then the function outputs adapted Potjans parameters. +""" +function potjans_params(ccu) + # a cummulative cell count + #cumulative = Dict{String, Vector{Int64}}() + layer_names = @SVector ["23E","23I","4E","4I","5E", "5I", "6E", "6I"] + # Probabilities for >=1 connection between neurons in the given populations. + # The first index is for the target population; the second for the source population + # 2/3e 2/3i 4e 4i 5e 5i 6e 6i + conn_probs = @SMatrix [0.1009 0.1689 0.0437 0.0818 0.0323 0.0 0.0076 0. + 0.1346 0.1371 0.0316 0.0515 0.0755 0. 0.0042 0. + 0.0077 0.0059 0.0497 0.135 0.0067 0.0003 0.0453 0. + 0.0691 0.0029 0.0794 0.1597 0.0033 0. 0.1057 0. + 0.1004 0.0622 0.0505 0.0057 0.0831 0.3726 0.0204 0. + 0.0548 0.0269 0.0257 0.0022 0.06 0.3158 0.0086 0. + 0.0156 0.0066 0.0211 0.0166 0.0572 0.0197 0.0396 0.2252 + 0.0364 0.001 0.0034 0.0005 0.0277 0.008 0.0658 0.1443 ] + + + # https://github.com/shimoura/ReScience-submission/blob/ShimouraR-KamijiNL-PenaRFO-CordeiroVL-CeballosCC-RomaroC-RoqueAC-2017/code/netParams.py + #= conn_probs = @SMatrix [0.101 0.169 0.044 0.082 0.032, 0. 0.008 0. 0. + 0.135 0.137 0.032, 0.052, 0.075, 0., 0.004 0. 0. + 0.008 0.006 0.050, 0.135, 0.007, 0.0003, 0.045 0. 0.0983 + 0.069 0.003 0.079, 0.160, 0.003, 0., 0.106 0. 0.0619 + 0.100 0.062 0.051, 0.006, 0.083, 0.373, 0.020 0. 0. + 0.055 0.027 0.026, 0.002, 0.060, 0.316, 0.009 0. 0. + 0.016 0.007 0.021, 0.017, 0.057, 0.020, 0.040 0.225 0.0512 + 0.036 0.001 0.003, 0.001, 0.028, 0.008, 0.066 0.144 0.0196 ] + =# + # hard coded network wiring parameters are manipulated below: + + syn_pol = Vector{Bool}([]) + for (i,syn) in enumerate(layer_names) + if occursin("E",syn) + push!(syn_pol,true) + else + push!(syn_pol,false) + end + end + #syn_pol = syn_pol # synaptic polarity vector. + return (conn_probs,syn_pol) +end + + +""" +Auxillary method, NB, this acts like the connectome constructor, so change function name to something more meaningful, like construct PotjanAndDiesmon +A mechanism for scaling cell population sizes to suite hardware constraints. +While Int64 might seem excessive when cell counts are between 1million to a billion Int64 is required. +Only dealing with positive count entities so Usigned is fine. +""" +function potjans_constructor(scale::Float64) + ccu = Dict{String, UInt32}("23E"=>20683, + "4E"=>21915, + "5E"=>4850, + "6E"=>14395, + "6I"=>2948, + "23I"=>5834, + "5I"=>1065, + "4I"=>5479) + + ccu = Dict{String, UInt32}((k,ceil(Int64,v*scale)) for (k,v) in pairs(ccu)) + v_old=1 + K = length(keys(ccu)) + cum_array = []# Vector{Array{UInt32}}(undef,K) + + for (k,v) in pairs(ccu) + ## update the cummulative cell count + push!(cum_array,v_old:v+v_old) + v_old=v+v_old + + end + + cum_array = SVector{8,Array{UInt32}}(cum_array) # cumulative population counts array. + Ncells = UInt64(sum([i for i in values(ccu)])+1) + Ne = UInt64(sum([ccu["23E"],ccu["4E"],ccu["5E"],ccu["6E"]])) + Ni = UInt64(Ncells - Ne) + (Ncells, Ne, Ni, ccu, cum_array) +end +""" +The entry point to building the whole Potjans model in SNN.jl +Also some of the current density parameters needed to adjust synaptic gain initial values. +Some of the following calculations and parameters are borrowed from this repository: +https://github.com/SpikingNetwork/TrainSpikingNet.jl/blob/master/src/param.jl +""" +function potjans_layer(scale::Float64) + (Ncells, Ne, Ni, ccu, cum_array)= potjans_constructor(scale) + (conn_probs,syn_pol) = potjans_params(ccu) + + pree = 0.1 + K = round(Int, Ne*pree) + sqrtK = sqrt(K) + g = 1.0 + tau_meme = 10 # (ms) + je = 2.0 / sqrtK * tau_meme * g + ji = je#2.0 / sqrtK * tau_meme * g + jee = 0.15je + jei = je + jie = -0.75ji + jii = -ji + #g_strengths = Vector{Float32}([jee,jie,jei,jii]) + Lxx = spzeros(Float32, (Ncells, Ncells)) + + + cell_index_to_layer::Vector{UInt64} = zeros(Ncells) + build_matrix_prot!(cell_index_to_layer,jee,jei,jie,jii,Lxx,cum_array,conn_probs,syn_pol)#,g_strengths) + Lxx,cell_index_to_layer + +end +export potjans_layer + + + + +""" +This function contains synapse selection logic seperated from iteration logic for readability only. +Used inside the nested iterator inside build_matrix. +Ideally iteration could flatten to support the readability of subsequent code. +""" +function build_matrix_prot!(cell_index_to_layer,jee::Real,jei::Real,jie::Real,jii::Real,Lxx::SparseMatrixCSC{Float32, Int64},cum_array::SVector{8, Array{UInt32}}, conn_probs::StaticArraysCore.SMatrix{8, 8, Float64, 64}, syn_pol::Vector{Bool})#, g_strengths::Vector{Float32}) + # excitatory weights. + + @inbounds @showprogress for (i,v) in enumerate(cum_array) + @inbounds for (j,v1) in enumerate(cum_array) + prob = conn_probs[i,j] + @inbounds for src in v + cell_index_to_layer[src] = i + @inbounds for tgt in v1 + if src!=tgt + if rand()nc0Max + nc0Max=templength + end + end + # outdegree + nc0 = Int.(nc0Max*ones(Ncells)) + ## + # Force ragged array into smallest dense rectangle (contains zeros for undefined synapses) + ## + w0Index = zeros(Int64, (nc0Max,Ncells)) + + #w0Index = spzeros(Int64,nc0Max,Ncells) + for pre_cell = 1:Ncells + post_cells = edge_dict[pre_cell] + stride_length = length(edge_dict[pre_cell]) + w0Index[1:stride_length,pre_cell] = post_cells + end + nc0,w0Index +end +""" +function genStaticWeights(args) + (edge_dict,w0Weights,Ne,Ni,Lexc,Linh) = potjans_weights(args) + #dropzeros!(w0Weights) + Ncells = args[1] + #nc0,w0Index = build_w0Index(edge_dict,Ncells) + return w0Index, w0Weights, nc0 +end \ No newline at end of file From e89e6f61001464c95746b2ab71ad49b5e3479d5c Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Wed, 13 Nov 2024 10:23:23 +1100 Subject: [PATCH 02/43] debugged potjans wiring file --- examples/genPotjansConnectivity.jl | 311 +++++++++++++---------------- 1 file changed, 136 insertions(+), 175 deletions(-) diff --git a/examples/genPotjansConnectivity.jl b/examples/genPotjansConnectivity.jl index 7792fea..99e10aa 100644 --- a/examples/genPotjansConnectivity.jl +++ b/examples/genPotjansConnectivity.jl @@ -1,203 +1,164 @@ using SparseArrays -using StaticArrays using ProgressMeter -using LinearAlgebra +using Plots """ -This file contains a function stack that creates a network with Potjans and Diesmann wiring likeness in Julia using SpikingNeuralNetworks.jl to simulate -electrical neural network dynamics. -This code draws heavily on the PyNN OSB Potjans implementation code found here: -https://github.com/OpenSourceBrain/PotjansDiesmann2014/blob/master/PyNN/network_params.py#L139-L146 -However the translation of this Python PyNN code into performant and scalable Julia was not trivial. -Hard coded Potjans parameters follow. -and then the function outputs adapted Potjans parameters. +Define Potjans parameters for neuron populations and connection probabilities """ -function potjans_params(ccu) - # a cummulative cell count - #cumulative = Dict{String, Vector{Int64}}() - layer_names = @SVector ["23E","23I","4E","4I","5E", "5I", "6E", "6I"] - # Probabilities for >=1 connection between neurons in the given populations. - # The first index is for the target population; the second for the source population - # 2/3e 2/3i 4e 4i 5e 5i 6e 6i - conn_probs = @SMatrix [0.1009 0.1689 0.0437 0.0818 0.0323 0.0 0.0076 0. - 0.1346 0.1371 0.0316 0.0515 0.0755 0. 0.0042 0. - 0.0077 0.0059 0.0497 0.135 0.0067 0.0003 0.0453 0. - 0.0691 0.0029 0.0794 0.1597 0.0033 0. 0.1057 0. - 0.1004 0.0622 0.0505 0.0057 0.0831 0.3726 0.0204 0. - 0.0548 0.0269 0.0257 0.0022 0.06 0.3158 0.0086 0. - 0.0156 0.0066 0.0211 0.0166 0.0572 0.0197 0.0396 0.2252 - 0.0364 0.001 0.0034 0.0005 0.0277 0.008 0.0658 0.1443 ] - - - # https://github.com/shimoura/ReScience-submission/blob/ShimouraR-KamijiNL-PenaRFO-CordeiroVL-CeballosCC-RomaroC-RoqueAC-2017/code/netParams.py - #= conn_probs = @SMatrix [0.101 0.169 0.044 0.082 0.032, 0. 0.008 0. 0. - 0.135 0.137 0.032, 0.052, 0.075, 0., 0.004 0. 0. - 0.008 0.006 0.050, 0.135, 0.007, 0.0003, 0.045 0. 0.0983 - 0.069 0.003 0.079, 0.160, 0.003, 0., 0.106 0. 0.0619 - 0.100 0.062 0.051, 0.006, 0.083, 0.373, 0.020 0. 0. - 0.055 0.027 0.026, 0.002, 0.060, 0.316, 0.009 0. 0. - 0.016 0.007 0.021, 0.017, 0.057, 0.020, 0.040 0.225 0.0512 - 0.036 0.001 0.003, 0.001, 0.028, 0.008, 0.066 0.144 0.0196 ] - =# - # hard coded network wiring parameters are manipulated below: - - syn_pol = Vector{Bool}([]) - for (i,syn) in enumerate(layer_names) - if occursin("E",syn) - push!(syn_pol,true) - else - push!(syn_pol,false) - end +function potjans_params(ccu, scale=1.0) + cumulative = Dict{String, Vector{Int64}}() + layer_names = ["23E", "23I", "4E", "4I", "5E", "5I", "6E", "6I"] + + # Replace static matrix with a regular matrix for `conn_probs` + conn_probs = Float32[ + 0.1009 0.1689 0.0437 0.0818 0.0323 0.0 0.0076 0.0 + 0.1346 0.1371 0.0316 0.0515 0.0755 0.0 0.0042 0.0 + 0.0077 0.0059 0.0497 0.135 0.0067 0.0003 0.0453 0.0 + 0.0691 0.0029 0.0794 0.1597 0.0033 0.0 0.1057 0.0 + 0.1004 0.0622 0.0505 0.0057 0.0831 0.3726 0.0204 0.0 + 0.0548 0.0269 0.0257 0.0022 0.06 0.3158 0.0086 0.0 + 0.0156 0.0066 0.0211 0.0166 0.0572 0.0197 0.0396 0.2252 + 0.0364 0.001 0.0034 0.0005 0.0277 0.008 0.0658 0.1443 + ] + + # Calculate cumulative cell counts + v_old = 1 + for (k, v) in pairs(ccu) + cumulative[k] = collect(v_old:v + v_old) + v_old += v + end + + syn_pol = Vector{Int64}(undef, length(ccu)) + for (i, k) in enumerate(keys(ccu)) + syn_pol[i] = occursin("E", k) ? 1 : 0 end - #syn_pol = syn_pol # synaptic polarity vector. - return (conn_probs,syn_pol) + + return cumulative, ccu, layer_names, conn_probs, syn_pol end - """ -Auxillary method, NB, this acts like the connectome constructor, so change function name to something more meaningful, like construct PotjanAndDiesmon -A mechanism for scaling cell population sizes to suite hardware constraints. -While Int64 might seem excessive when cell counts are between 1million to a billion Int64 is required. -Only dealing with positive count entities so Usigned is fine. +Assign synapse weights selectively, and only update non-zero entries """ -function potjans_constructor(scale::Float64) - ccu = Dict{String, UInt32}("23E"=>20683, - "4E"=>21915, - "5E"=>4850, - "6E"=>14395, - "6I"=>2948, - "23I"=>5834, - "5I"=>1065, - "4I"=>5479) +function index_assignment!(item, w0Weights, g_strengths, Lee, Lie, Lii, Lei) + (src, tgt, syn0, syn1) = item + jee, jie, jei, jii = g_strengths + wig = -20 * 4.5f0 + + if syn0 == 1 && syn1 == 1 + w0Weights[src, tgt] = jee + Lee[src, tgt] = jee + elseif syn0 == 1 && syn1 == 0 + w0Weights[src, tgt] = jei + Lei[src, tgt] = jei + elseif syn0 == 0 && syn1 == 1 + w0Weights[src, tgt] = wig + Lie[src, tgt] = wig + elseif syn0 == 0 && syn1 == 0 + w0Weights[src, tgt] = wig + Lii[src, tgt] = wig + end +end - ccu = Dict{String, UInt32}((k,ceil(Int64,v*scale)) for (k,v) in pairs(ccu)) - v_old=1 - K = length(keys(ccu)) - cum_array = []# Vector{Array{UInt32}}(undef,K) +""" +Build matrix with memory-efficient computations, filtering and batching +""" +function build_matrix(cumulative, conn_probs, Ncells, g_strengths, syn_pol, batch_size=1000) + w0Weights = spzeros(Float32, Ncells, Ncells) + Lee = spzeros(Float32, Ncells, Ncells) + Lii = spzeros(Float32, Ncells, Ncells) + Lei = spzeros(Float32, Ncells, Ncells) + Lie = spzeros(Float32, Ncells, Ncells) + + cumvalues = collect(values(cumulative)) + + # Process connections in batches + @inbounds @showprogress for batch in Iterators.partition(1:length(cumvalues), batch_size) + for i in batch + (syn0, v) = (syn_pol[i], cumvalues[i]) + for src in v + for j in batch + prob = conn_probs[i, j] + if prob > 0 + (syn1, v1) = (syn_pol[j], cumvalues[j]) + for tgt in v1 + if src != tgt && rand() < prob + item = (src, tgt, syn0, syn1) + index_assignment!(item, w0Weights, g_strengths, Lee, Lie, Lii, Lei) + end + end + end + end + end + end + end - for (k,v) in pairs(ccu) - ## update the cummulative cell count - push!(cum_array,v_old:v+v_old) - v_old=v+v_old + Lexc = Lee + Lei + Linh = Lie + Lii + + return w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh +end - end +""" +Create Potjans weights with modularized params and memory-optimized matrices +""" +function potjans_weights(Ncells, g_strengths, ccu, scale) + cumulative, ccu, layer_names, conn_probs, syn_pol = potjans_params(ccu, scale) + build_matrix(cumulative, conn_probs, Ncells, g_strengths, syn_pol) +end - cum_array = SVector{8,Array{UInt32}}(cum_array) # cumulative population counts array. - Ncells = UInt64(sum([i for i in values(ccu)])+1) - Ne = UInt64(sum([ccu["23E"],ccu["4E"],ccu["5E"],ccu["6E"]])) - Ni = UInt64(Ncells - Ne) - (Ncells, Ne, Ni, ccu, cum_array) +""" +Auxiliary Potjans parameters for neural populations with scaled cell counts +""" +function auxil_potjans_param(scale=1.0) + ccu = Dict( + "23E" => trunc(Int32, 20683 * scale), + "4E" => trunc(Int32, 21915 * scale), + "5E" => trunc(Int32, 4850 * scale), + "6E" => trunc(Int32, 14395 * scale), + "6I" => trunc(Int32, 2948 * scale), + "23I" => trunc(Int32, 5834 * scale), + "5I" => trunc(Int32, 1065 * scale), + "4I" => trunc(Int32, 5479 * scale) + ) + + Ncells = trunc(Int32, sum(values(ccu)) + 1) + Ne = trunc(Int32, ccu["23E"] + ccu["4E"] + ccu["5E"] + ccu["6E"]) + Ni = Ncells - Ne + return Ncells, Ne, Ni, ccu end """ -The entry point to building the whole Potjans model in SNN.jl -Also some of the current density parameters needed to adjust synaptic gain initial values. -Some of the following calculations and parameters are borrowed from this repository: -https://github.com/SpikingNetwork/TrainSpikingNet.jl/blob/master/src/param.jl +Main function to setup Potjans layer with memory-optimized connectivity """ -function potjans_layer(scale::Float64) - (Ncells, Ne, Ni, ccu, cum_array)= potjans_constructor(scale) - (conn_probs,syn_pol) = potjans_params(ccu) - +function potjans_layer(scale) + Ncells, Ne, Ni, ccu = auxil_potjans_param(scale) + + # Synaptic strengths for each connection type pree = 0.1 - K = round(Int, Ne*pree) + K = round(Int, Ne * pree) sqrtK = sqrt(K) g = 1.0 - tau_meme = 10 # (ms) + tau_meme = 10.0 # (ms) je = 2.0 / sqrtK * tau_meme * g - ji = je#2.0 / sqrtK * tau_meme * g - jee = 0.15je - jei = je - jie = -0.75ji + ji = 2.0 / sqrtK * tau_meme * g + jee = 0.15 * je + jei = je + jie = -0.75 * ji jii = -ji - #g_strengths = Vector{Float32}([jee,jie,jei,jii]) - Lxx = spzeros(Float32, (Ncells, Ncells)) - + g_strengths = Float32[jee, jie, jei, jii] - cell_index_to_layer::Vector{UInt64} = zeros(Ncells) - build_matrix_prot!(cell_index_to_layer,jee,jei,jie,jii,Lxx,cum_array,conn_probs,syn_pol)#,g_strengths) - Lxx,cell_index_to_layer - -end -export potjans_layer - - - - -""" -This function contains synapse selection logic seperated from iteration logic for readability only. -Used inside the nested iterator inside build_matrix. -Ideally iteration could flatten to support the readability of subsequent code. -""" -function build_matrix_prot!(cell_index_to_layer,jee::Real,jei::Real,jie::Real,jii::Real,Lxx::SparseMatrixCSC{Float32, Int64},cum_array::SVector{8, Array{UInt32}}, conn_probs::StaticArraysCore.SMatrix{8, 8, Float64, 64}, syn_pol::Vector{Bool})#, g_strengths::Vector{Float32}) - # excitatory weights. - - @inbounds @showprogress for (i,v) in enumerate(cum_array) - @inbounds for (j,v1) in enumerate(cum_array) - prob = conn_probs[i,j] - @inbounds for src in v - cell_index_to_layer[src] = i - @inbounds for tgt in v1 - if src!=tgt - if rand()nc0Max - nc0Max=templength - end - end - # outdegree - nc0 = Int.(nc0Max*ones(Ncells)) - ## - # Force ragged array into smallest dense rectangle (contains zeros for undefined synapses) - ## - w0Index = zeros(Int64, (nc0Max,Ncells)) +# Extract the matrix you want to plot +matrix_to_plot = layer_matrices[1] # Replace 1 with the index of the matrix you need - #w0Index = spzeros(Int64,nc0Max,Ncells) - for pre_cell = 1:Ncells - post_cells = edge_dict[pre_cell] - stride_length = length(edge_dict[pre_cell]) - w0Index[1:stride_length,pre_cell] = post_cells - end - nc0,w0Index -end -""" -function genStaticWeights(args) - (edge_dict,w0Weights,Ne,Ni,Lexc,Linh) = potjans_weights(args) - #dropzeros!(w0Weights) - Ncells = args[1] - #nc0,w0Index = build_w0Index(edge_dict,Ncells) - return w0Index, w0Weights, nc0 -end \ No newline at end of file +# Plot the heatmap +heatmap(matrix_to_plot, color=:viridis, xlabel="Source Neurons", ylabel="Target Neurons", title="Connectivity Matrix Heatmap") From 58083c4c30bab8817206bf60992c2fc785e6786c Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Mon, 25 Nov 2024 18:25:29 +1100 Subject: [PATCH 03/43] integrating my code --- examples/genPotjansConnectivity.jl | 427 +++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 examples/genPotjansConnectivity.jl diff --git a/examples/genPotjansConnectivity.jl b/examples/genPotjansConnectivity.jl new file mode 100644 index 0000000..436177d --- /dev/null +++ b/examples/genPotjansConnectivity.jl @@ -0,0 +1,427 @@ +using Revise +using DrWatson +@quickactivate "SpikingNeuralNetworks" +using SpikingNeuralNetworks +SNN.@load_units +import SpikingNeuralNetworks: AdExParameter, IFParameter +using Statistics, Random +using Plots +using SparseArrays +using ProgressMeter +using Plots +using SpikingNeuralNetworks + + +""" +Define Potjans parameters for neuron populations and connection probabilities +""" +function potjans_params(ccu, scale=1.0) + cumulative = Dict{String, Vector{Int64}}() + layer_names = ["23E", "23I", "4E", "4I", "5E", "5I", "6E", "6I"] + + # Replace static matrix with a regular matrix for `conn_probs` + conn_probs = Float32[ + 0.1009 0.1689 0.0437 0.0818 0.0323 0.0 0.0076 0.0 + 0.1346 0.1371 0.0316 0.0515 0.0755 0.0 0.0042 0.0 + 0.0077 0.0059 0.0497 0.135 0.0067 0.0003 0.0453 0.0 + 0.0691 0.0029 0.0794 0.1597 0.0033 0.0 0.1057 0.0 + 0.1004 0.0622 0.0505 0.0057 0.0831 0.3726 0.0204 0.0 + 0.0548 0.0269 0.0257 0.0022 0.06 0.3158 0.0086 0.0 + 0.0156 0.0066 0.0211 0.0166 0.0572 0.0197 0.0396 0.2252 + 0.0364 0.001 0.0034 0.0005 0.0277 0.008 0.0658 0.1443 + ] + + # Calculate cumulative cell counts + v_old = 1 + for (k, v) in pairs(ccu) + cumulative[k] = collect(v_old:v + v_old) + v_old += v + end + + syn_pol = Vector{Int64}(undef, length(ccu)) + for (i, k) in enumerate(keys(ccu)) + syn_pol[i] = occursin("E", k) ? 1 : 0 + end + @show(syn_pol) + return cumulative, ccu, layer_names, conn_probs, syn_pol +end + +""" +Assign synapse weights selectively, and only update non-zero entries +""" +function index_assignment!(item, w0Weights, g_strengths, Lee, Lie, Lii, Lei) + (src, tgt, syn0, syn1) = item + jee, jie, jei, jii = g_strengths + wig = -20 * 4.5f0 + + if syn0 == 1 && syn1 == 1 + w0Weights[src, tgt] = jee + Lee[src, tgt] = jee + elseif syn0 == 1 && syn1 == 0 + w0Weights[src, tgt] = jei + Lei[src, tgt] = jei + elseif syn0 == 0 && syn1 == 1 + w0Weights[src, tgt] = wig + Lie[src, tgt] = wig + elseif syn0 == 0 && syn1 == 0 + w0Weights[src, tgt] = wig + Lii[src, tgt] = wig + end + + println("Non-zero entries in the excitatory weight matrix:") + println(count(!iszero, Lee)) + println(count(!iszero, w0Weights)) + +end + +""" +Build matrix with memory-efficient computations, filtering and batching +""" +function build_matrix(cumulative, conn_probs, Ncells, g_strengths, syn_pol, batch_size=1000) + w0Weights = spzeros(Float32, Ncells, Ncells) + Lee = spzeros(Float32, Ncells, Ncells) + Lii = spzeros(Float32, Ncells, Ncells) + Lei = spzeros(Float32, Ncells, Ncells) + Lie = spzeros(Float32, Ncells, Ncells) + + cumvalues = collect(values(cumulative)) + + # Process connections in batches + @inbounds @showprogress for batch in Iterators.partition(1:length(cumvalues), batch_size) + for i in batch + (syn0, v) = (syn_pol[i], cumvalues[i]) + for src in v + for j in batch + prob = conn_probs[i, j] + if prob > 0 + (syn1, v1) = (syn_pol[j], cumvalues[j]) + for tgt in v1 + if src != tgt && rand() < prob + item = (src, tgt, syn0, syn1) + index_assignment!(item, w0Weights, g_strengths, Lee, Lie, Lii, Lei) + end + end + end + end + end + end + end + + Lexc = Lee + Lei + @show(Lexc) + Linh = Lie + Lii + @show(Linh) + + return w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh +end + +""" +Create Potjans weights with modularized params and memory-optimized matrices +""" +function potjans_weights(Ncells, g_strengths, ccu, scale) + cumulative, ccu, layer_names, conn_probs, syn_pol = potjans_params(ccu, scale) + w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh = build_matrix(cumulative, conn_probs, Ncells, g_strengths, syn_pol) + w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh +end + +""" +Auxiliary Potjans parameters for neural populations with scaled cell counts +""" +function auxil_potjans_param(scale=1.0) + ccu = Dict( + "23E" => trunc(Int32, 20683 * scale), + "4E" => trunc(Int32, 21915 * scale), + "5E" => trunc(Int32, 4850 * scale), + "6E" => trunc(Int32, 14395 * scale), + "6I" => trunc(Int32, 2948 * scale), + "23I" => trunc(Int32, 5834 * scale), + "5I" => trunc(Int32, 1065 * scale), + "4I" => trunc(Int32, 5479 * scale) + ) + + Ncells = trunc(Int32, sum(values(ccu)) + 1) + Ne = trunc(Int32, ccu["23E"] + ccu["4E"] + ccu["5E"] + ccu["6E"]) + @show(Ne) + Ni = Ncells - Ne + return Ncells, Ne, Ni, ccu +end +""" +Main function to setup Potjans layer with memory-optimized connectivity +""" +function potjans_layer(scale) + Ncells, Ne, Ni, ccu = auxil_potjans_param(scale) + + # Synaptic strengths for each connection type + pree = 0.1 + K = round(Int, Ne * pree) + sqrtK = sqrt(K) + g = 1.0 + tau_meme = 10.0 # (ms) + je = 2.0 / sqrtK * tau_meme * g + ji = 2.0 / sqrtK * tau_meme * g + jee = 0.15 * je + jei = je + jie = -0.75 * ji + jii = -ji + g_strengths = Float32[jee, jie, jei, jii] + #@show(g_strengths) + + w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh = potjans_weights(Ncells, g_strengths, ccu, scale) +end + +# Run with a specific scaling factor +scale = 0.01 +w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh = potjans_layer(scale) + +exc_pop = unique(Lexc.rowval) +inhib_pop = unique(Linh.rowval) + + +exc_pop = Set(Lexc.rowval) +inhib_pop = Set(Linh.rowval) + + +# Find overlapping elements +overlap = intersect(exc_pop, inhib_pop) + +# Remove overlapping elements from both sets +Epop_numbers = setdiff(exc_pop, overlap) +IPop_numbers = setdiff(inhib_pop, overlap) + +overlap = intersect(exc_pop, inhib_pop) + +#= +Note I may have to remove these elements from Lee,Lie,Lii,Lexc,Linh + +=# + +# Ensure they are disjoint (no overlap) +if isempty(overlap) + println("exc_pop and inhib_pop are unique sets.") +else + println("exc_pop and inhib_pop have overlapping elements: ", overlap) +end + + +## Neuron parameters + +function initialize_LKD(Epop_numbers,IPop_numbers,w0Weights, Lee, Lie, Lei, Lii,νe = 4.5Hz) + τm = 20ms + C = 300SNN.pF # Capacitance + R = τm / C + τre = 1ms # Rise time for excitatory synapses + τde = 6ms # Decay time for excitatory synapses + τri = 0.5ms # Rise time for inhibitory synapses + τdi = 2ms # Decay time for inhibitory synapses + + # Input and synapse paramater + N = 1000 + # νe = 8.5Hz # Rate of external input to E neurons + # νe = 4.5Hz # Rate of external input to E neurons + νi = 0#0.025Hz # Rate of external input to I neurons + p_in = 0.5 #1.0 # 0.5 + μ_in_E = 1.78SNN.pF + + μEE = 2.76SNN.pF # Initial E to E synaptic weight + μIE = 48.7SNN.pF # Initial I to E synaptic weight + μEI = 1.27SNN.pF # Synaptic weight from E to I + μII = 16.2SNN.pF # Synaptic weight from I to I + + Random.seed!(28) + + LKD_AdEx_exc = AdExParameter( + τm = 20ms, + Vt = -52mV, + Vr = -60mV, + El = -70mV, + R = R, + ΔT = 2mV, + a = 4nS, + b = 0.805SNN.pA, + τabs = 1ms, + τw = 150ms, + τre = τre, + τde = τde, + τri = τri, + τdi = τdi, + At = 10mV, + τT = 30ms, + E_i = -75mV, + E_e = 0mV, + ) # 0.000805nA + LKD_IF_inh = IFParameter( + τm = 20ms, + Vt = -52mV, + Vr = -60mV, + El = -62mV, + R = R, + τre = τre, + τde = τde, + τri = τri, + τdi = τdi, + E_i = -75mV, + E_e = 0mV, + ) + + Input_E = SNN.Poisson(; N = N, param = SNN.PoissonParameter(; rate = νe)) + Input_I = SNN.Poisson(; N = N, param = SNN.PoissonParameter(; rate = νi)) + E = SNN.AdEx(; N = 800, param = LKD_AdEx_exc) + I = SNN.IF(; N = 200, param = LKD_IF_inh) + EE = SNN.SpikingSynapse(E, E, w = Lee, :ge; μ = μEE, param = SNN.vSTDPParameter()) + EI = SNN.SpikingSynapse(E, I, w = Lei, :ge; μ = μEI) + IE = SNN.SpikingSynapse(I, E, w = Lie, :gi; μ = μIE, param = SNN.iSTDPParameter()) + II = SNN.SpikingSynapse(I, I, w = Lii, :gi; μ = μII) + ProjI = SNN.SpikingSynapse(Input_I, I, :ge; μ = μ_in_E, p = p_in) + ProjE = SNN.SpikingSynapse(Input_E, E, :ge; μ = μ_in_E, p = p_in) # connection from input to E + P = [E, I, Input_E, Input_I] + C = [EE, II, EI, IE, ProjE, ProjI] + return P, C +end + +## +P,C = initialize_LKD(Epop_numbers,IPop_numbers,w0Weights, Lee, Lie, Lei, Lii,νe = 4.5Hz) + + +# +#P, C = initialize_LKD(20Hz) +duration = 15000ms +pltdur = 500e1 +SNN.monitor(P[1:2], [:fire, :v]) +SNN.sim!(P, C; duration = duration) +SNN.raster(P[1:2], [1, 1.5] .* pltdur) +SNN.vecplot(P[1], :v, 10) +# E_neuron_spikes = map(sum, E.records[:fire]) +# E_neuron_spikes = map(sum, E.records[:fire]) +histogram(IE.W[:]) + +## +function firing_rate(E, I, bin_width, bin_edges) + # avg spikes at each time step + E_neuron_spikes = map(sum, E.records[:fire]) ./ E.N + I_neuron_spikes = map(sum, I.records[:fire]) ./ E.N + # Count the number of spikes in each bin + E_bin_count = [sum(E_neuron_spikes[i:(i+bin_width-1)]) for i in bin_edges] + I_bin_count = [sum(I_neuron_spikes[i:(i+bin_width-1)]) for i in bin_edges] + return E_bin_count, I_bin_count +end + +# frequencies = [1Hz 10Hz 100Hz] +inputs = 0:50:300 +E_bin_counts = [] +I_bin_counts = [] +bin_edges = [] +bin_width = 10 # in milliseconds + +num_bins = Int(length(E.records[:fire]) / bin_width) +bin_edges = 1:bin_width:(num_bins*bin_width) + +## + +## +inputs = 0nA:5e3nA:(50e3)nA +rates = zeros(length(inputs), 2) +for (n, inh_input) in enumerate(inputs) + # νe = 25Hz + # νi = 3Hz + # N =10000 + # p_in = 0.2 + @info inh_input + + # Input_I = SNN.Poisson(; N = N, param = SNN.PoissonParameter(; rate = νi)) + # Input_E = SNN.Poisson(; N = N, param = SNN.PoissonParameter(; rate = νe)) + # E = SNN.AdEx(; N = 500, param = LKD_AdEx_exc) + # I = SNN.IF(; N = 125, param = LKD_IF_inh) + # EE = SNN.SpikingSynapse(E, E, :ge; μ = μEE, p = 0.2) + # EI = SNN.SpikingSynapse(E, I, :ge; μ = μEI, p = 0.2) + # IE = SNN.SpikingSynapse(I, E, :gi; μ = μIE, p = 0.2) + # II = SNN.SpikingSynapse(I, I, :gi; μ = μII, p = 0.2) + # ProjE = SNN.SpikingSynapse(Input_E, E, :ge; μ = μ_in_E, p = p_in) + # ProjI = SNN.SpikingSynapse(Input_I, I, :ge; μ = μ_in_E, p = p_in) + + + + # + duration = 500ms + P, C = initialize_LKD(20Hz) + SNN.monitor(P[1:2], [:fire]) + SNN.sim!(P, C; duration = duration) + + duration = 5000ms + P[2].I .= inh_input + SNN.sim!(P, C; duration = duration) + E_bin_count, I_bin_count = firing_rate(P[1], P[2], bin_width, bin_edges) + rates[n, 1] = mean(E_bin_count[(end-100):end]) + rates[n, 2] = mean(I_bin_count[(end-100):end]) + # push!(E_bin_counts, E_bin_count) + # push!(I_bin_counts, I_bin_count) +end + +plot( + inputs, + rates, + label = ["Excitatory" "Inhibitory"], + ylabel = "Firing rate (Hz)", + xlabel = "External input (μA)", +) +## + +# Create a new plot or use an existing plot if it exists +plot( + xlabel = "Time bins", + size = (800, 800), + ylabel = "Firing frequency (spikes/$(bin_width) ms)", + xtickfontsize = 6, + ytickfontsize = 6, + yguidefontsize = 6, + xguidefontsize = 6, + titlefontsize = 7, + legend = :bottomright, + layout = (length(frequencies), 1), + title = ["νi = $(νi*1000)Hz" for νi in frequencies], +) + +# Plot excitatory neurons +plot!(bin_edges, E_bin_counts, label = "Excitatory neurons") +# Plot inhibitory neurons +plot!(bin_edges, I_bin_counts, label = "Inhibitory neurons") + + +# Optional: If you need them as sorted arrays (not sets) +#exc_pop_array = sort(collect(exc_pop)) +#inhib_pop_array = sort(collect(inhib_pop)) + +#@show exc_pop_array +#@show inhib_pop_array + +# Assuming `layer_matrices` is a tuple of matrices, such as: +# layer_matrices = (w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh) +# For example, let's plot the first matrix, `w0Weights` + +# Extract the matrix you want to plot +matrix_to_plot = w0Weights[1] # Replace 1 with the index of the matrix you need + +#= +# Plot the heatmap +heatmap(matrix_to_plot, color=:viridis, xlabel="Source Neurons", ylabel="Target Neurons", title="Connectivity Matrix Heatmap") + +# Assuming `matrix_to_plot` is the matrix you want to visualize +matrix_to_plot = layer_matrices[1] # For example, w0Weights + +# Convert the matrix to dense if it's sparse +matrix_dense = Matrix(matrix_to_plot) + +# Normalize the matrix between 0 and 1 +min_val = minimum(matrix_dense) +max_val = maximum(matrix_dense) +normalized_matrix = (matrix_dense .- min_val) ./ (max_val - min_val) + +# Plot the normalized heatmap +heatmap( + normalized_matrix, + color=:viridis, + xlabel="Source Neurons", + ylabel="Target Neurons", + title="Normalized Connectivity Matrix Heatmap", + clims=(0, 1) # Setting color limits for the normalized scale +) +=# \ No newline at end of file From 1c504726e97fc9b43440cac21115400d1fa22188 Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Thu, 28 Nov 2024 10:44:46 +1100 Subject: [PATCH 04/43] update code --- examples/PotjansDiesman.jl | 217 +++++++++++++++++++++++++++++++ examples/batch_nmnist_motions.py | 65 +++++++++ examples/getNMNIST.jl | 84 ++++++++++++ 3 files changed, 366 insertions(+) create mode 100644 examples/PotjansDiesman.jl create mode 100644 examples/batch_nmnist_motions.py create mode 100644 examples/getNMNIST.jl diff --git a/examples/PotjansDiesman.jl b/examples/PotjansDiesman.jl new file mode 100644 index 0000000..9547275 --- /dev/null +++ b/examples/PotjansDiesman.jl @@ -0,0 +1,217 @@ +using Revise +using DrWatson +using SpikingNeuralNetworks +SNN.@load_units +import SpikingNeuralNetworks: AdExParameter, IFParameter +using Statistics, Random +using Plots +using SparseArrays +using ProgressMeter +using Plots +using SpikingNeuralNetworks +using SNNUtils + + +#using Graphs +#using Karnak +#using NetworkLayout +#using Colors + +""" +Auxiliary Potjans parameters for neural populations with scaled cell counts +""" +function potjans_neurons(scale=1.0) + ccu = Dict( + :E23 => trunc(Int32, 20683 * scale), + :E4 => trunc(Int32, 21915 * scale), + :E5 => trunc(Int32, 4850 * scale), + :E6 => trunc(Int32, 14395 * scale), + :I6 => trunc(Int32, 2948 * scale), + :I23 => trunc(Int32, 5834 * scale), + :I5 => trunc(Int32, 1065 * scale), + :I4 => trunc(Int32, 5479 * scale) + ) + + neurons = Dict{Symbol, SNN.AbstractPopulation}() + for (k, v) in ccu + if occursin("E", String(k)) + neurons[k] = SNN.AdEx(; N = 1, param = SNN.AdExParameter(; El = -49mV), name=string(k)) + + else + + neurons[k] = SNN.IF(; N = 1, param = SNN.IFParameter(; El = -49mV), name=string(k)) + end + end + return neurons +end + +""" +Define Potjans parameters for neuron populations and connection probabilities + # Names of the simulated populations. + 'populations': ['L23E', 'L23I', 'L4E', 'L4I', 'L5E', 'L5I', 'L6E', 'L6I'], + # Number of neurons in the different populations. The order of the + # elements corresponds to the names of the variable 'populations'. + 'N_full': np.array([20683, 5834, 21915, 5479, 4850, 1065, 14395, 2948]), + # Mean rates of the different populations in the non-scaled version + # of the microcircuit. Necessary for the scaling of the network. + # The order corresponds to the order in 'populations'. + 'full_mean_rates': + np.array([0.971, 2.868, 4.746, 5.396, 8.142, 9.078, 0.991, 7.523]), + # Connection probabilities. The first index corresponds to the targets + # and the second to the sources. + # Number of external connections to the different populations. + # The order corresponds to the order in 'populations'. + 'K_ext': np.array([1600, 1500, 2100, 1900, 2000, 1900, 2900, 2100]), + # Factor to scale the indegrees. + 'K_scaling': 0.1, + # Factor to scale the number of neurons. + 'N_scaling': 0.1, + # Mean amplitude of excitatory postsynaptic potential (in mV). + 'PSP_e': 0.15, + # Relative standard deviation of the postsynaptic potential. + 'PSP_sd': 0.1, + # Relative inhibitory synaptic strength (in relative units). + 'g': -4, + # Rate of the Poissonian spike generator (in Hz). + 'bg_rate': 8., + # Turn Poisson input on or off (True or False). + 'poisson_input': True, + # Delay of the Poisson generator (in ms). + 'poisson_delay': 1.5, + # Mean delay of excitatory connections (in ms). + 'mean_delay_exc': 1.5, + # Mean delay of inhibitory connections (in ms). + 'mean_delay_inh': 0.75, + # Relative standard deviation of the delay of excitatory and + # inhibitory connections (in relative units). + 'rel_std_delay': 0.5, + """ +function potjans_conn(Ne) + + function j_from_name(pre, post, g) + if occursin("E", String(pre)) && occursin("E", String(post)) + return g.jee + elseif occursin("I", String(pre)) && occursin("E", String(post)) + return g.jei + elseif occursin("E", String(pre)) && occursin("I", String(post)) + return g.jie + elseif occursin("I", String(pre)) && occursin("I", String(post)) + return g.jii + else + throw(ArgumentError("Invalid pre-post combination: $pre-$post")) + end + end + + layer_names = [:E23, :I23, :E4, :I4, :E5, :I5, :E6, :I6] + + # Replace static matrix with a regular matrix for `conn_probs` + # ! the convention is j_post_pre. This is how the matrices `w` are built. Are you using that when defining the parameters? + conn_probs = Float32[ + 0.1009 0.1689 0.0437 0.0818 0.0323 0.0 0.0076 0.0 + 0.1346 0.1371 0.0316 0.0515 0.0755 0.0 0.0042 0.0 + 0.0077 0.0059 0.0497 0.135 0.0067 0.0003 0.0453 0.0 + 0.0691 0.0029 0.0794 0.1597 0.0033 0.0 0.1057 0.0 + 0.1004 0.0622 0.0505 0.0057 0.0831 0.3726 0.0204 0.0 + 0.0548 0.0269 0.0257 0.0022 0.06 0.3158 0.0086 0.0 + 0.0156 0.0066 0.0211 0.0166 0.0572 0.0197 0.0396 0.2252 + 0.0364 0.001 0.0034 0.0005 0.0277 0.008 0.0658 0.1443 + ] + + # !Assign dimensions to these parameters, and some reference + pree = 0.2 + g = 1.0 + tau_meme = 10.0 # (ms) + # Synaptic strengths for each connection type + K = round(Int, Ne * pree) + sqrtK = sqrt(K) + + # !Same, the convention is j_post_pre + je = 2.0 / sqrtK * tau_meme * g + ji = 2.0 / sqrtK * tau_meme * g + jee = 0.15 * je + jie = je + jei = -0.75 * ji + jii = -ji + g_strengths = dict2ntuple(SNN.@symdict jee jie jei jii) + + conn_j = zeros(Float32, size(conn_probs)) + for pre in eachindex(layer_names) + for post in eachindex(layer_names) + conn_j[post, pre ] = j_from_name(layer_names[pre], layer_names[post], g_strengths) + end + end + + return layer_names, conn_probs, conn_j +end + + +""" +Main function to setup Potjans layer with memory-optimized connectivity +""" +function potjans_layer(scale) + + ## Create the neuron populations + neurons = potjans_neurons(scale) + exc_pop = filter(x -> occursin("E", String(x)), keys(neurons)) + inh_pop = filter(x -> occursin("I", String(x)), keys(neurons)) + Ne = trunc(Int32, sum([neurons[k].N for k in exc_pop])) + Ni = trunc(Int32, sum([neurons[k].N for k in inh_pop])) + #@show exc_pop, Ne, Ni + layer_names, conn_probs, conn_j = potjans_conn(Ne) + + ## Create the synaptic connections based on the connection probabilities and synaptic weights assigned to each pre-post pair + connections = Dict() + for i in eachindex(layer_names) + for j in eachindex(layer_names) + pre = layer_names[i] + post = layer_names[j] + p = conn_probs[j, i] + J = conn_j[j, i] + sym = J>=0 ? :ge : :gi + μ = abs(J) + + + #S = SNN.SpikingSynapse( + # inputs, + # neurons, + # :ge; + # μ = 0.01, + # p = 1.0, + # param = SNN.vSTDPParameter(; Wmax = 0.01), + #) + + s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ*10, p=p, σ=0)#,param = SNN.vSTDPParameter(; Wmax = 0.5)) + connections[Symbol(string(pre,"_", post))] = s + end + end + + ## Create the Poisson stimulus for each population + stimuli = Dict() + for pop in exc_pop + νe = 3.5kHz + post = neurons[pop] + s = SNN.PoissonStimulus(post, :ge; param = νe, cells=:ALL, μ=1.f0, name="PoissonE_$(post.name)") + stimuli[Symbol(string("PoissonE_", pop))] = s + end + return merge_models(neurons,connections, stimuli) +end + + +model = potjans_layer(0.1) +duration = 15000ms +SNN.monitor([model.pop...], [:fire]) +SNN.monitor([model.pop...], [:v])#, sr=200Hz) +SNN.sim!(model=model; duration = duration, pbar = true, dt = 0.125) +display(SNN.raster(model.pop))#, [10s, 15s])) + +#Trange = 0:10:15s +frE, interval = SNN.firing_rate(model.pop, interval = Trange) +display(plot(mean.(frE), xlabel="Time [ms]", ylabel="Firing rate [Hz]", legend=:topleft)) +## + +#vecplot(model.pop.E23, :v, neurons =1, r=0s:15s,label="soma") +layer_names, conn_probs, conn_j = potjans_conn(4000) +pj = heatmap(conn_j, xticks=(1:8,layer_names), yticks=(1:8,layer_names), aspect_ratio=1, color=:bluesreds, title="Synaptic weights", xlabel="Presynaptic", ylabel="Postsynaptic", size=(500,500), clims=(-maximum(abs.(conn_j)), maximum(abs.(conn_j)))) +pprob=heatmap(conn_probs, xticks=(1:8,layer_names), yticks=(1:8,layer_names), aspect_ratio=1, color=:viridis, title="Connection probability", xlabel="Presynaptic", ylabel="Postsynaptic", size=(500,500)) +display(plot(pprob, pj, layout=(1,2), size=(1000,500), margin=5Plots.mm)) +## \ No newline at end of file diff --git a/examples/batch_nmnist_motions.py b/examples/batch_nmnist_motions.py new file mode 100644 index 0000000..2d7d612 --- /dev/null +++ b/examples/batch_nmnist_motions.py @@ -0,0 +1,65 @@ +""" + +NMNIST_Motions dataset class + +Provides access to the event-based motions NMIST dataset. This is a version of the +NMNIST dataset in which we have separated out the events by motion and linked them +to the specified MNIST images. This allows us to retrieve any motion from the dataset +along with the associated mnist image. + +""" +from os.path import exists + +#import h5py +import numpy as np +import tonic +from numpy.lib.recfunctions import merge_arrays + + +class NMNIST(object): + """ A Dataset interface to the NMNIST dataset """ + + def __init__(self, dataset_path, train=True, first_saccade_only=False,transform=None): + """ Creates a dataset instance using the specified path """ + super(NMNIST, self).__init__() + + # Validate the specified path and store it + if not exists(dataset_path): + raise Exception("Specified dataset path does not exist") + self._dataset = tonic.datasets.NMNIST(save_to='./data', train=train, first_saccade_only=first_saccade_only,transform=transform) + # self.transform = transform + + def get_dataset_item(self, indices): + + assert(len(indices) <= 100) + all_events = [] + + for id,index in enumerate(indices): + (grid_x,grid_y) = np.unravel_index(id,(10,10)) + + events, label = self._dataset[index] + label_array = np.full(events['x'].shape[0],label,dtype=[('label','i8')]) + event_array = merge_arrays((events,label_array),flatten=True) + event_array['x'] = grid_x*36 + event_array['x'] + 1 + event_array['y'] = grid_y*36 + event_array['y'] + 1 + # event_array[:,3] -= event_array[0,3] + all_events.append(event_array) + + super_events = np.hstack(all_events) + super_events = super_events[super_events['t'].argsort()] + + + return super_events + + + def get_count(self): + """ Returns the number of items """ + return len(self._dataset) + + def get_label_range(self): + """ Returns the range of labels in this dataset """ + return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + def get_element_dimensions(self): + """ Returns a tuple containing the dimensions of each image in the dataset """ + return self._dataset.sensor_size \ No newline at end of file diff --git a/examples/getNMNIST.jl b/examples/getNMNIST.jl new file mode 100644 index 0000000..0821a7e --- /dev/null +++ b/examples/getNMNIST.jl @@ -0,0 +1,84 @@ +using JLD2 + + + +function bds!() + + pushfirst!(PyVector(pyimport("sys")."path"), "") + nmnist_module = pyimport("batch_nmnist_motions") + dataset::PyObject = nmnist_module.NMNIST("./") + training_order = shuffle(0:dataset.get_count()-1) + #storage = Array{Array}([]) + storage::Array{Tuple{Vector{UInt32}, Vector{UInt32}, Vector{Float32}, Vector{Int8}, Vector{UInt32}, Vector{Any}}} = [] + storage = [] + input_shape = dataset.get_element_dimensions() + cnt = 0 + + @time @inbounds for batch in 1:100:length(training_order) + if cnt<1400 + + events = dataset.get_dataset_item(training_order[batch:batch+100-1]) + cnt,did_it_exec = build_data_set_native(Odesa.ConvOdesa,events,storage,cnt,input_shape) + push!(storage,did_it_exec) + end + end + #@load "yeshes_events.jld" events_cache input_shape + + @save "all_mnmist.jld" storage + + +end + + +function expected_spike_format(empty_spike_cont,nodes1,times1,maxt) + nodes1 = [i+1 for i in nodes1] + + @inbounds for i in collect(1:1220) + @inbounds for (neuron, t) in zip(nodes1,times1) + if i == neuron + push!(empty_spike_cont[Int32(i)],Float32(t)+Float32(maxt)) + end + end + end + empty_spike_cont,minimum(empty_spike_cont),maximum(empty_spike_cont) +end + +function NMNIST_pre_process_spike_data(temp_container_store;duration=25) + spike_packet_lists = Vector{Any}([]) + labelsl = Vector{Any}([]) + packet_window_boundaries = Vector{Any}([]) + maxt = 0 + empty_spike_cont = [] + @inbounds for i in collect(1:1220) + push!(empty_spike_cont,[]) + end + cnt = 0 + @inbounds @showprogress for (ind,s) in enumerate(temp_container_store) + (times,labels,nodes) = (s[1],s[2],s[3]) + maxt = maximum(times) + if length(times) != 0 + if cnt Date: Thu, 28 Nov 2024 10:47:50 +1100 Subject: [PATCH 05/43] update merge --- Project.toml | 8 ++ examples/genPotjansConnectivity.jl | 169 +++++++++++++++-------------- 2 files changed, 98 insertions(+), 79 deletions(-) diff --git a/Project.toml b/Project.toml index 8a4b94e..dbd18f1 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,8 @@ version = "0.1.1" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" Dagger = "d58978e5-989f-55fb-8d15-ea34adc7bf54" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" DrWatson = "634d3b9d-ee7a-5ddf-bec9-22491ea816e1" @@ -20,18 +22,24 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Requires = "ae029012-a4dd-5104-9daa-d747884805df" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" RollingFunctions = "b0e4dd01-7b14-53d8-9b45-175a3e362653" +SGtSNEpi = "e6c19c8d-e382-4a50-b2c6-174ddd647730" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" ThreadTools = "dbf13d8f-d36e-4350-8970-f3a99faba1a8" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" [compat] +CairoMakie = "0.12.16" +Colors = "0.12.11" Distributions = "0.25.112" Graphs = "1.12.0" MetaGraphs = "0.8.0" Requires = "0.5, 1" +SGtSNEpi = "0.4.0" +StatsBase = "0.34.3" UnPack = "1" julia = "1" diff --git a/examples/genPotjansConnectivity.jl b/examples/genPotjansConnectivity.jl index 436177d..6876986 100644 --- a/examples/genPotjansConnectivity.jl +++ b/examples/genPotjansConnectivity.jl @@ -11,6 +11,15 @@ using ProgressMeter using Plots using SpikingNeuralNetworks +using SGtSNEpi, Random +using Revise +using Colors, LinearAlgebra +#using GLMakie +using Graphs +using JLD2 +import StatsBase.mean + + """ Define Potjans parameters for neuron populations and connection probabilities @@ -42,7 +51,7 @@ function potjans_params(ccu, scale=1.0) for (i, k) in enumerate(keys(ccu)) syn_pol[i] = occursin("E", k) ? 1 : 0 end - @show(syn_pol) + #@show(syn_pol) return cumulative, ccu, layer_names, conn_probs, syn_pol end @@ -67,11 +76,6 @@ function index_assignment!(item, w0Weights, g_strengths, Lee, Lie, Lii, Lei) w0Weights[src, tgt] = wig Lii[src, tgt] = wig end - - println("Non-zero entries in the excitatory weight matrix:") - println(count(!iszero, Lee)) - println(count(!iszero, w0Weights)) - end """ @@ -108,9 +112,9 @@ function build_matrix(cumulative, conn_probs, Ncells, g_strengths, syn_pol, batc end Lexc = Lee + Lei - @show(Lexc) + #@show(Lexc) Linh = Lie + Lii - @show(Linh) + #@show(Linh) return w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh end @@ -141,7 +145,7 @@ function auxil_potjans_param(scale=1.0) Ncells = trunc(Int32, sum(values(ccu)) + 1) Ne = trunc(Int32, ccu["23E"] + ccu["4E"] + ccu["5E"] + ccu["6E"]) - @show(Ne) + #@show(Ne) Ni = Ncells - Ne return Ncells, Ne, Ni, ccu end @@ -169,26 +173,29 @@ function potjans_layer(scale) w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh = potjans_weights(Ncells, g_strengths, ccu, scale) end -# Run with a specific scaling factor -scale = 0.01 -w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh = potjans_layer(scale) - -exc_pop = unique(Lexc.rowval) -inhib_pop = unique(Linh.rowval) +if !isfile("potjans_wiring.jld") + # Run with a specific scaling factor + scale = 0.01 + w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh = potjans_layer(scale) + @save "potjans_wiring.jld" w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh +else + @load "potjans_wiring.jld" w0Weights, Lee, Lie, Lei, Lii, Lexc, Linh -exc_pop = Set(Lexc.rowval) -inhib_pop = Set(Linh.rowval) + exc_pop = unique(Lexc.rowval) + inhib_pop = unique(Linh.rowval) + exc_pop = Set(Lexc.rowval) + inhib_pop = Set(Linh.rowval) -# Find overlapping elements -overlap = intersect(exc_pop, inhib_pop) + # Find overlapping elements + overlap = intersect(exc_pop, inhib_pop) -# Remove overlapping elements from both sets -Epop_numbers = setdiff(exc_pop, overlap) -IPop_numbers = setdiff(inhib_pop, overlap) + # Remove overlapping elements from both sets + Epop_numbers = setdiff(exc_pop, overlap) + IPop_numbers = setdiff(inhib_pop, overlap) -overlap = intersect(exc_pop, inhib_pop) + overlap = intersect(exc_pop, inhib_pop) #= Note I may have to remove these elements from Lee,Lie,Lii,Lexc,Linh @@ -205,7 +212,7 @@ end ## Neuron parameters -function initialize_LKD(Epop_numbers,IPop_numbers,w0Weights, Lee, Lie, Lei, Lii,νe = 4.5Hz) +function initialize_LKD(Epop_numbers,IPop_numbers,w0Weights, Lee, Lie, Lei, Lii;νe = 4.5Hz) τm = 20ms C = 300SNN.pF # Capacitance R = τm / C @@ -245,7 +252,7 @@ function initialize_LKD(Epop_numbers,IPop_numbers,w0Weights, Lee, Lie, Lei, Lii, τri = τri, τdi = τdi, At = 10mV, - τT = 30ms, + τt = 30ms, E_i = -75mV, E_e = 0mV, ) # 0.000805nA @@ -265,34 +272,80 @@ function initialize_LKD(Epop_numbers,IPop_numbers,w0Weights, Lee, Lie, Lei, Lii, Input_E = SNN.Poisson(; N = N, param = SNN.PoissonParameter(; rate = νe)) Input_I = SNN.Poisson(; N = N, param = SNN.PoissonParameter(; rate = νi)) - E = SNN.AdEx(; N = 800, param = LKD_AdEx_exc) - I = SNN.IF(; N = 200, param = LKD_IF_inh) + E = SNN.AdEx(; N = size(Lee)[1], param = LKD_AdEx_exc) + I = SNN.IF(; N = size(Lee)[1], param = LKD_IF_inh) EE = SNN.SpikingSynapse(E, E, w = Lee, :ge; μ = μEE, param = SNN.vSTDPParameter()) - EI = SNN.SpikingSynapse(E, I, w = Lei, :ge; μ = μEI) - IE = SNN.SpikingSynapse(I, E, w = Lie, :gi; μ = μIE, param = SNN.iSTDPParameter()) + EI = SNN.SpikingSynapse(E, I, w = Lei, :ge; μ = μEI, param = SNN.vSTDPParameter()) + IE = SNN.SpikingSynapse(I, E, w = Lie, :gi; μ = μIE, param = SNN.vSTDPParameter()) II = SNN.SpikingSynapse(I, I, w = Lii, :gi; μ = μII) ProjI = SNN.SpikingSynapse(Input_I, I, :ge; μ = μ_in_E, p = p_in) ProjE = SNN.SpikingSynapse(Input_E, E, :ge; μ = μ_in_E, p = p_in) # connection from input to E P = [E, I, Input_E, Input_I] C = [EE, II, EI, IE, ProjE, ProjI] - return P, C + return P, C, EE, II, EI, IE, ProjE, ProjI end ## -P,C = initialize_LKD(Epop_numbers,IPop_numbers,w0Weights, Lee, Lie, Lei, Lii,νe = 4.5Hz) +P,C, EE, II, EI, IE, ProjE, ProjI = initialize_LKD(Epop_numbers,IPop_numbers,w0Weights, Lee, Lie, Lei, Lii;νe = 4.5Hz) # #P, C = initialize_LKD(20Hz) duration = 15000ms -pltdur = 500e1 +#pltdur = 500e1 SNN.monitor(P[1:2], [:fire, :v]) SNN.sim!(P, C; duration = duration) -SNN.raster(P[1:2], [1, 1.5] .* pltdur) -SNN.vecplot(P[1], :v, 10) + +p1 = SNN.raster(P[1]) +p2 = SNN.raster(P[2]) +display(plot(p1,p2)) + +matrix_to_plot = w0Weights # Replace 1 with the index of the matrix you need + + +# Plot the heatmap +heatmap(matrix_to_plot, color=:viridis, xlabel="Source Neurons", ylabel="Target Neurons", title="Connectivity Matrix Heatmap") + +# Assuming `matrix_to_plot` is the matrix you want to visualize +#matrix_to_plot = layer_matrices[1] # For example, w0Weights +final = Matrix(Lee)+Matrix(Lei) +# Convert the matrix to dense if it's sparse +matrix_dense = Matrix(final) + +# Normalize the matrix between 0 and 1 +min_val = minimum(matrix_dense) +max_val = maximum(matrix_dense) +normalized_matrix = (matrix_dense .- min_val) ./ (max_val - min_val) + +# Plot the normalized heatmap +heatmap( + normalized_matrix, + color=:viridis, + xlabel="Source Neurons", + ylabel="Target Neurons", + title="Normalized Connectivity Matrix Heatmap", + clims=(0, 1) # Setting color limits for the normalized scale +) + +#scale = 0.015 +#pot_conn = grab_connectome(scale) +dim = 2 +Lx = Vector{Int64}(zeros(size(w0Weights[2,:]))) +Lx = convert(Vector{Int64},Lx) + +y = sgtsnepi(w0Weights) +cmap_out = distinguishable_colors( + length(Lx), + [RGB(1,1,1), RGB(0,0,0)], dropseed=true) + +display(SGtSNEpi.show_embedding( y, Lx ,A=w0Weights;edge_alpha=0.15,lwd_in=0.15,lwd_out=0.013,cmap=cmap_out) +#SNN.raster(P[1:2], [1, 1.5] .* pltdur) +#display(SNN.vecplot(P[1], :v, 10)) # E_neuron_spikes = map(sum, E.records[:fire]) # E_neuron_spikes = map(sum, E.records[:fire]) -histogram(IE.W[:]) +display(histogram(IE.W[:])) +display(histogram(EE.W[:])) +display(histogram(EI.W[:])) ## function firing_rate(E, I, bin_width, bin_edges) @@ -312,7 +365,7 @@ I_bin_counts = [] bin_edges = [] bin_width = 10 # in milliseconds -num_bins = Int(length(E.records[:fire]) / bin_width) +num_bins = Int(length(EE.records[:fire]) / bin_width) bin_edges = 1:bin_width:(num_bins*bin_width) ## @@ -321,28 +374,13 @@ bin_edges = 1:bin_width:(num_bins*bin_width) inputs = 0nA:5e3nA:(50e3)nA rates = zeros(length(inputs), 2) for (n, inh_input) in enumerate(inputs) - # νe = 25Hz - # νi = 3Hz - # N =10000 - # p_in = 0.2 @info inh_input - # Input_I = SNN.Poisson(; N = N, param = SNN.PoissonParameter(; rate = νi)) - # Input_E = SNN.Poisson(; N = N, param = SNN.PoissonParameter(; rate = νe)) - # E = SNN.AdEx(; N = 500, param = LKD_AdEx_exc) - # I = SNN.IF(; N = 125, param = LKD_IF_inh) - # EE = SNN.SpikingSynapse(E, E, :ge; μ = μEE, p = 0.2) - # EI = SNN.SpikingSynapse(E, I, :ge; μ = μEI, p = 0.2) - # IE = SNN.SpikingSynapse(I, E, :gi; μ = μIE, p = 0.2) - # II = SNN.SpikingSynapse(I, I, :gi; μ = μII, p = 0.2) - # ProjE = SNN.SpikingSynapse(Input_E, E, :ge; μ = μ_in_E, p = p_in) - # ProjI = SNN.SpikingSynapse(Input_I, I, :ge; μ = μ_in_E, p = p_in) - # duration = 500ms - P, C = initialize_LKD(20Hz) + #P, C = initialize_LKD(20Hz) SNN.monitor(P[1:2], [:fire]) SNN.sim!(P, C; duration = duration) @@ -398,30 +436,3 @@ plot!(bin_edges, I_bin_counts, label = "Inhibitory neurons") # For example, let's plot the first matrix, `w0Weights` # Extract the matrix you want to plot -matrix_to_plot = w0Weights[1] # Replace 1 with the index of the matrix you need - -#= -# Plot the heatmap -heatmap(matrix_to_plot, color=:viridis, xlabel="Source Neurons", ylabel="Target Neurons", title="Connectivity Matrix Heatmap") - -# Assuming `matrix_to_plot` is the matrix you want to visualize -matrix_to_plot = layer_matrices[1] # For example, w0Weights - -# Convert the matrix to dense if it's sparse -matrix_dense = Matrix(matrix_to_plot) - -# Normalize the matrix between 0 and 1 -min_val = minimum(matrix_dense) -max_val = maximum(matrix_dense) -normalized_matrix = (matrix_dense .- min_val) ./ (max_val - min_val) - -# Plot the normalized heatmap -heatmap( - normalized_matrix, - color=:viridis, - xlabel="Source Neurons", - ylabel="Target Neurons", - title="Normalized Connectivity Matrix Heatmap", - clims=(0, 1) # Setting color limits for the normalized scale -) -=# \ No newline at end of file From 258ce27008daa16b7ce0c5d2dbc9838b6b52cd6c Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Thu, 5 Dec 2024 16:12:57 +1100 Subject: [PATCH 06/43] key changes --- examples/Tonic_NMNIST_Stimulus.jl | 98 ++++++ examples/batch_nmnist_motions.py | 13 +- examples/potjans_nmnist.jl | 522 ++++++++++++++++++++++++++++++ 3 files changed, 630 insertions(+), 3 deletions(-) create mode 100644 examples/Tonic_NMNIST_Stimulus.jl create mode 100644 examples/potjans_nmnist.jl diff --git a/examples/Tonic_NMNIST_Stimulus.jl b/examples/Tonic_NMNIST_Stimulus.jl new file mode 100644 index 0000000..0e1c61f --- /dev/null +++ b/examples/Tonic_NMNIST_Stimulus.jl @@ -0,0 +1,98 @@ +module Tonic_NMNIST_Stimulus + using JLD2 + using PyCall + using DataFrames + + using Random + using Plots + """ + NMNIST data set is a 3D labelled data set played out onto a 400 by 400 matrix of pixels over time. For any time instant there can be positive and negative spikes. + Here the 2D pixel matrix is collapsed into 1D so that it the matrix can be more easily projected onto the synapses of a biological network population. + """ + + """ + Call Pythons tonic module to get NMNIST a spiking data set of numerals. + """ + + function getNMNIST(ind)#, cnt, input_shape) + pushfirst!(PyVector(pyimport("sys")."path"), "") + nmnist_module = pyimport("batch_nmnist_motions") + dataset::PyObject = nmnist_module.NMNIST("./") + A = zeros((35,35)) + I = LinearIndices(A) + pop_stimulation= Vector{Int32}([])#Vector{UInt32}([]) + l = -1 + events,l = dataset._dataset[ind]#._dataset(ind)#;limit=15) + l = convert(Int32,l) + x = Vector{Int32}([e[1] for e in events]) + y = Vector{Int32}([e[2] for e in events]) + ts = Vector{Float32}([e[3]/10000.0 for e in events]) + p = Vector{Int8}([e[4] for e in events]) + for (x_,y_) in zip(x,y) + push!(pop_stimulation,Int32(I[CartesianIndex(convert(Int32,x_+1),convert(Int32,y_+1))])) + end + (ts,p,l,pop_stimulation) + end + + function spike_packets_by_labels(cached_spikes) + ts = [cached_spikes[i][1] for (i,l) in enumerate(cached_spikes)] + p = [cached_spikes[i][2] for (i,l) in enumerate(cached_spikes)] + labels = [cached_spikes[i][3] for (i,l) in enumerate(cached_spikes)] + pop_stimulation = [cached_spikes[i][4] for (i,l) in enumerate(cached_spikes)] + + # Create DataFrame + df = DataFrame( + ts = ts, + p = p, + labels = labels, + pop_stimulation = pop_stimulation + ) + + # Sort the DataFrame by `labels` + sorted_df = sort(df, :labels) + + # Group by `labels` to create indexed/enumerated groups + grouped_df = groupby(sorted_df, :labels) + unique_labels = unique(labels) + labeled_packets = Dict() + for filter_label in unique_labels + time_and_offset=[] + population_code=[] + next_offset = 0 + group = grouped_df[filter_label+1] + for row in eachrow(group) + append!(population_code,row["pop_stimulation"]) + append!(time_and_offset,next_offset.+row["ts"]) + windowb = minimum(row["ts"]) + windowa = maximum(row["ts"]) + next_offset = windowa-windowb + + + labeled_packets[filter_label] = (population_code,time_and_offset) + + end + end + labeled_packets + end + + """ + For a given numeral characters label plot the corresponding spike packets. + """ + function spike_character_plot(filter_label,population_code,time_and_offset) + p1 = Plots.scatter() + scatter!(p1, + population_code, + time_and_offset, + ms = 1, # Marker size + ylabel = "Time (ms)", + xlabel = "Neuron Index", + title = "Spiking Activity with Distinct Characters", + legend=false + ) + plot(p1) + savefig("label$filter_label.png") + end +end + + + diff --git a/examples/batch_nmnist_motions.py b/examples/batch_nmnist_motions.py index 2d7d612..a999c8e 100644 --- a/examples/batch_nmnist_motions.py +++ b/examples/batch_nmnist_motions.py @@ -35,13 +35,20 @@ def get_dataset_item(self, indices): all_events = [] for id,index in enumerate(indices): - (grid_x,grid_y) = np.unravel_index(id,(10,10)) + #(grid_x,grid_y) = np.unravel_index(id,(10,10)) events, label = self._dataset[index] + #print(events) label_array = np.full(events['x'].shape[0],label,dtype=[('label','i8')]) event_array = merge_arrays((events,label_array),flatten=True) - event_array['x'] = grid_x*36 + event_array['x'] + 1 - event_array['y'] = grid_y*36 + event_array['y'] + 1 + ## + # Its possible that the 36 is depreciated. + ## + #event_array['x'] = grid_x + event_array['x'] + 1 + #event_array['y'] = grid_y + event_array['y'] + 1 + #event_array['x'] = grid_x*36 + event_array['x'] + 1 + #event_array['y'] = grid_y*36 + event_array['y'] + 1 + # event_array[:,3] -= event_array[0,3] all_events.append(event_array) diff --git a/examples/potjans_nmnist.jl b/examples/potjans_nmnist.jl new file mode 100644 index 0000000..30d1222 --- /dev/null +++ b/examples/potjans_nmnist.jl @@ -0,0 +1,522 @@ +using Revise +using DrWatson +using SpikingNeuralNetworks +SNN.@load_units +import SpikingNeuralNetworks: AdExParameter, IFParameter +using Statistics, Random +using Plots +using SparseArrays +using ProgressMeter +using Plots +using SpikingNeuralNetworks +using SNNUtils +using JLD2 +include("Tonic_NMNIST_Stimulus.jl") +using .Tonic_NMNIST_Stimulus + + + + +""" +https://github.com/OpenSourceBrain/PotjansDiesmann2014/blob/master/PyNN/network_params.py#L139-L146 +Creates matrix containing the delay of all connections. + +Arguments +--------- +mean_delay_exc + Delay of the excitatory connections. +mean_delay_inh + Delay of the inhibitory connections. +number_of_pop + Number of populations. + +Returns +------- +mean_delays + Matrix specifying the mean delay of all connections. + +""" +function get_mean_delays(mean_delay_exc, mean_delay_inh, number_of_pop) + + dim = number_of_pop + mean_delays = np.zeros((dim, dim)) + mean_delays[:, 0:dim:2] = mean_delay_exc + mean_delays[:, 1:dim:2] = mean_delay_inh + return mean_delays + +""" +https://github.com/OpenSourceBrain/PotjansDiesmann2014/blob/master/PyNN/network_params.py#L139-L146 +Creates matrix containing the standard deviations of all delays. + +Arguments +--------- +std_delay_exc + Standard deviation of excitatory delays. +std_delay_inh + Standard deviation of inhibitory delays. +number_of_pop + Number of populations in the microcircuit. + +Returns +------- +std_delays + Matrix specifying the standard deviation of all delays. + +""" +function get_std_delays(std_delay_exc, std_delay_inh, number_of_pop) + + dim = number_of_pop + std_delays = np.zeros((dim, dim)) + std_delays[:, 0:dim:2] = std_delay_exc + std_delays[:, 1:dim:2] = std_delay_inh + return std_delays + +""" +https://github.com/OpenSourceBrain/PotjansDiesmann2014/blob/master/PyNN/network_params.py#L139-L146 +Creates a matrix of the mean evoked postsynaptic potential. + +The function creates a matrix of the mean evoked postsynaptic +potentials between the recurrent connections of the microcircuit. +The weight of the connection from L4E to L23E is doubled. + +Arguments +--------- +PSP_e + Mean evoked potential. +g + Relative strength of the inhibitory to excitatory connection. +number_of_pop + Number of populations in the microcircuit. + +Returns +------- +weights + Matrix of the weights for the recurrent connections. + +""" + +function get_mean_PSP_matrix(PSP_e, g, number_of_pop) + dim = number_of_pop + weights = zeros((dim, dim)) + exc = PSP_e + inh = PSP_e * g + weights[:, 0:dim:2] = exc + weights[:, 1:dim:2] = inh + weights[1, 3] = exc * 2 + return weights + +""" +https://github.com/OpenSourceBrain/PotjansDiesmann2014/blob/master/PyNN/network_params.py#L139-L146 +Relative standard deviation matrix of postsynaptic potential created. + +The relative standard deviation matrix of the evoked postsynaptic potential +for the recurrent connections of the microcircuit is created. + +Arguments +--------- +PSP_rel + Relative standard deviation of the evoked postsynaptic potential. +number_of_pop + Number of populations in the microcircuit. + +Returns +------- +std_mat + Matrix of the standard deviation of postsynaptic potentials. + +""" + +function get_std_PSP_matrix(PSP_rel, number_of_pop) + dim = number_of_pop + std_mat = zeros((dim, dim)) + std_mat[:, :] = PSP_rel + return std_mat + + +""" +Auxiliary Potjans parameters for neural populations with scaled cell counts +""" +function potjans_neurons(scale=1.0) + ccu = Dict( + :E23 => trunc(Int32, 20683 * scale), + :E4 => trunc(Int32, 21915 * scale), + :E5 => trunc(Int32, 4850 * scale), + :E6 => trunc(Int32, 14395 * scale), + :I6 => trunc(Int32, 2948 * scale), + :I23 => trunc(Int32, 5834 * scale), + :I5 => trunc(Int32, 1065 * scale), + :I4 => trunc(Int32, 5479 * scale) + ) + + neurons = Dict{Symbol, SNN.AbstractPopulation}() + for (k, v) in ccu + if occursin("E", String(k)) + neurons[k] = AdEx(N = v, param=LKD2014SingleExp.AdEx, name=string(k)) + else + neurons[k] = IF(N = v, param=LKD2014SingleExp.PV, name=string(k)) + end + end + return neurons +end + +""" +Define Potjans parameters for neuron populations and connection probabilities +""" +function potjans_conn(Ne) + + function j_from_name(pre, post, g) + if occursin("E", String(pre)) && occursin("E", String(post)) + return g.jee + elseif occursin("I", String(pre)) && occursin("E", String(post)) + return g.jei + elseif occursin("E", String(pre)) && occursin("I", String(post)) + return g.jie + elseif occursin("I", String(pre)) && occursin("I", String(post)) + return g.jii + else + throw(ArgumentError("Invalid pre-post combination: $pre-$post")) + end + end + + + + layer_names = [:E23, :I23, :E4, :I4, :E5, :I5, :E6, :I6] + + + + total_cortical_thickness = 1500.0 + N_full = [20683, 5834, 21915, 5479, 4850, 1065, 14395, 2948] + N_E_total = N_full[0]+N_full[2]+N_full[4]+N_full[6] + dimensions_3D = Dict( + "x_dimension"=> 1000, + "z_dimension"=> 1000, + "total_cortical_thickness"=> total_cortical_thickness, + + # Have the thicknesses proportional to the numbers of E cells in each layer + "layer_thicknesses"=> Dict( + "L23"=> total_cortical_thickness*N_full[0]/N_E_total, + "L4" => total_cortical_thickness*N_full[2]/N_E_total, + "L5" => total_cortical_thickness*N_full[4]/N_E_total, + "L6" => total_cortical_thickness*N_full[6]/N_E_total, + "thalamus" => 100 + ) + ) + net_dict = Dict( + "PSP_e"=> 0.15, + # Relative standard deviation of the postsynaptic potential. + "PSP_sd"=> 0.1, + # Relative inhibitory synaptic strength (in relative units). + "g"=> -4, + # Rate of the Poissonian spike generator (in Hz). + "bg_rate"=> 8., + # Turn Poisson input on or off (True or False). + "poisson_input"=> True, + # Delay of the Poisson generator (in ms). + "poisson_delay"=> 1.5, + # Mean delay of excitatory connections (in ms). + "mean_delay_exc"=> 1.5, + # Mean delay of inhibitory connections (in ms). + "mean_delay_inh"=> 0.75, + # Relative standard deviation of the delay of excitatory and + # inhibitory connections (in relative units). + "rel_std_delay"=> 0.5, + ) + updated_dict = Dict( + # PSP mean matrix. + "PSP_mean_matrix"=> get_mean_PSP_matrix( + net_dict['PSP_e'], net_dict['g'], len(layer_names) + ), + # PSP std matrix. + "PSP_std_matrix"=> get_std_PSP_matrix( + net_dict['PSP_sd'], len(layer_names) + ), + # mean delay matrix. + "mean_delay_matrix"=> get_mean_delays( + net_dict['mean_delay_exc'], net_dict['mean_delay_inh'], + len(layer_names) + ), + # std delay matrix. + "std_delay_matrix"=> get_std_delays( + net_dict['mean_delay_exc'] * net_dict['rel_std_delay'], + net_dict['mean_delay_inh'] * net_dict['rel_std_delay'], + len(layer_names]) + ), + ) + merge!(net_dict, updated_dict) + + # Replace static matrix with a regular matrix for `conn_probs` + # ! the convention is j_post_pre. This is how the matrices `w` are built. Are you using that when defining the parameters? + conn_probs = Float32[ + 0.1009 0.1689 0.0437 0.0818 0.0323 0.0 0.0076 0.0 + 0.1346 0.1371 0.0316 0.0515 0.0755 0.0 0.0042 0.0 + 0.0077 0.0059 0.0497 0.135 0.0067 0.0003 0.0453 0.0 + 0.0691 0.0029 0.0794 0.1597 0.0033 0.0 0.1057 0.0 + 0.1004 0.0622 0.0505 0.0057 0.0831 0.3726 0.0204 0.0 + 0.0548 0.0269 0.0257 0.0022 0.06 0.3158 0.0086 0.0 + 0.0156 0.0066 0.0211 0.0166 0.0572 0.0197 0.0396 0.2252 + 0.0364 0.001 0.0034 0.0005 0.0277 0.008 0.0658 0.1443 + ] + + # !Assign dimensions to these parameters, and some reference + pree = 0.1 + g = 1.0 + tau_meme = 10.0 # (ms) + # Synaptic strengths for each connection type + #= + K = round(Int, Ne * pree) + sqrtK = sqrt(K) + + # !Same, the convention is j_post_pre + je = 2.0 / sqrtK * tau_meme * g + ji = 2.0 / sqrtK * tau_meme * g + jee = 0.15 * je + jie = je + jei = -0.75 * ji + jii = -ji + =# + + jii = jie = -4.0 + jee = jei = 0.15 + g_strengths = dict2ntuple(SNN.@symdict jee jie jei jii) + + conn_j = zeros(Float32, size(conn_probs)) + for pre in eachindex(layer_names) + for post in eachindex(layer_names) + conn_j[post, pre ] = j_from_name(layer_names[pre], layer_names[post], g_strengths) + end + end + + return layer_names, conn_probs, conn_j +end + + +""" +Main function to setup Potjans layer with memory-optimized connectivity +""" +function potjans_layer(scale) + + ## Create the neuron populations + neurons = potjans_neurons(scale) + exc_pop = filter(x -> occursin("E", String(x)), keys(neurons)) + inh_pop = filter(x -> occursin("I", String(x)), keys(neurons)) + Ne = trunc(Int32, sum([neurons[k].N for k in exc_pop])) + Ni = trunc(Int32, sum([neurons[k].N for k in inh_pop])) + @show exc_pop, Ne, Ni + layer_names, conn_probs, conn_j = potjans_conn(Ne) + + ## Create the synaptic connections based on the connection probabilities and synaptic weights assigned to each pre-post pair + connections = Dict() + for i in eachindex(layer_names) + for j in eachindex(layer_names) + pre = layer_names[i] + post = layer_names[j] + p = conn_probs[j, i] + J = conn_j[j, i] + sym = J>=0 ? :ge : :gi + μ = abs(J) + s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ, p=p, σ=0) + connections[Symbol(string(pre,"_", post))] = s + end + end + + #'full_mean_rates': + full_mean_rates = [0.971, 2.868, 4.746, 5.396, 8.142, 9.078, 0.991, 7.523] + stimuli = Dict() + for (ind,pop) in enumerate(exc_pop) + νe = full_mean_rates[ind]kHz + post = neurons[pop] + s = SNN.PoissonStimulus(post, :ge; param = νe, cells=:ALL, μ=1.f0, name="PoissonE_$(post.name)") + stimuli[Symbol(string("PoissonE_", pop))] = s + end + return merge_models(neurons,connections, stimuli),neurons,connections,stimuli +end + +if !isfile("cached_potjans_model.jld2") + model,neurons_,connections_,stimuli_ = potjans_layer(0.125) + @save "cached_potjans_model.jld2" model neurons_ connections_ stimuli_ +else + @load "cached_potjans_model.jld2" model neurons_ connections_ stimuli_ +end +duration = 15000ms +SNN.monitor([model.pop...], [:fire]) +SNN.monitor([model.pop...], [:v], sr=200Hz) +SNN.sim!(model=model; duration = duration, pbar = true, dt = 0.125) +display(SNN.raster(model.pop, [0s, 15s])) +savefig("without_stimulus.png") + + + + + +training_order = shuffle(0:60000-1) + +cached_spikes=[] +for i in 1:40 + push!(cached_spikes,Tonic_NMNIST_Stimulus.getNMNIST(training_order[i])) +end + +""" +Filter for spike packets that belong to provided labels that pertain to single numeral characters. +""" + + +if !isfile("labeled_packets.jld2") + labeled_packets = Tonic_NMNIST_Stimulus.spike_packets_by_labels(cached_spikes) + filter_label = 0 + (population_code,time_and_offset) = labeled_packets[filter_label] + for filter_label in range(0,9) + (population_code_,time_and_offset_) = labeled_packets[filter_label] + append!(population_code,population_code_) + append!(time_and_offset,time_and_offset_) + end + @save "labeled_packets.jld2" population_code time_and_offset +else + @load "labeled_packets.jld2" population_code time_and_offset +end + + + +p1 = Plots.scatter() +scatter!(p1, +time_and_offset, +population_code, +ms = 1, # Marker size +ylabel = "Time (ms)", +xlabel = "Neuron Index", +title = "Spiking Activity with Distinct Characters", +legend=false +) +display(plot(p1)) +savefig("stimulus.png") + +neurons_as_nested_array = [ Vector{Int64}([n]) for n in population_code] +inputs = SpikeTime(time_and_offset,neurons_as_nested_array) + +st = neurons_[:E4] #Identity(N=max_neurons(inputs)) +w = ones(Float32,neurons_[:E4].N,max_neurons(inputs))*15 + + +st = Identity(N=max_neurons(inputs)) +stim = SpikeTimeStimulusIdentity(st, :g, param=inputs) +stdp_param = STDPParameter(A_pre =5e-1, + A_post=-5e-1, + τpre =20ms, + τpost =15ms) + +syn = SpikingSynapse( st, neurons_[:E4], nothing, w = w, param = stdp_param) +model2 = merge_models(pop=[st,model], stim=[stim,stimuli_], syn=[syn,connections_], silent=false) + +duration = 15000ms +SNN.monitor([model2.pop...], [:fire]) +SNN.monitor([model2.pop...], [:v], sr=200Hz) +SNN.sim!(model=model2; duration = duration, pbar = true, dt = 0.125) +display(SNN.raster(model2.pop, [0s, 15s])) +savefig("with_stimulus.png") + +Trange = 0:10:15s +frE, interval, names_pop = SNN.firing_rate(model2.pop, interval = Trange) +plot(mean.(frE), label=hcat(names_pop...), xlabel="Time [ms]", ylabel="Firing rate [Hz]", legend=:topleft) +savefig("firing_rate.png") + +## + +vecplot(model2.pop.E4, :v, neurons =1, r=0s:15s,label="soma") +savefig("vector_vm_plot.png") +layer_names, conn_probs, conn_j = potjans_conn(4000) +pj = heatmap(conn_j, xticks=(1:8,layer_names), yticks=(1:8,layer_names), aspect_ratio=1, color=:bluesreds, title="Synaptic weights", xlabel="Presynaptic", ylabel="Postsynaptic", size=(500,500), clims=(-maximum(abs.(conn_j)), maximum(abs.(conn_j)))) +pprob=heatmap(conn_probs, xticks=(1:8,layer_names), yticks=(1:8,layer_names), aspect_ratio=1, color=:viridis, title="Connection probability", xlabel="Presynaptic", ylabel="Postsynaptic", size=(500,500)) +plot(pprob, pj, layout=(1,2), size=(1000,500), margin=5Plots.mm) + +## + + #Tonic_NMNIST_Stimulus.spike_chara + #labels = cached_spikes[5][:] + #unique_labels = unique(labels) + #labeled_packets = Dict() + #for filter_label in unique_labels + # population_code,time_and_offset = Tonic_NMNIST_Stimulus.spike_packets_by_labels(filter_label,cached_spikes) + # labeled_packets[filter_label] = (population_code,time_and_offset) + # Tonic_NMNIST_Stimulus.spike_character_plot(filter_label,population_code,time_and_offset) + + #end + + #= + labels = cached_spikes[5][:] + unique_labels = unique(labels) + labeled_packets = Dict() + for filter_label in unique_labels + population_code,time_and_offset = Tonic_NMNIST_Stimulus.spike_packets_by_labels(filter_label,cached_spikes) + labeled_packets[filter_label] = (population_code,time_and_offset) + Tonic_NMNIST_Stimulus.spike_character_plot(filter_label,population_code,time_and_offset) + + end + =# + #scatter_plot(filter_label,population_code,time_and_offset) + + + +#array_size = maximum(unique_neurons) +#w = zeros(Float32, array_size,array_size) + +#w[population_code, population_code] = 1f0 + +#stparam = SpikeTimeStimulusParameter(time_and_offset,population_code) +#SpikeTimeStimulus(stim_size,neurons,param=stparam) +#= +function times_x_neurons(spikes::Spiketimes) + all_times = Dict{Float64, Vector{Int}}() + for n in eachindex(spikes) + if !isempty(spikes[n]) + for tt in spikes[n] + # tt = round(Int,t*1000/dt) ## from seconds to timesteps + if haskey(all_times,tt) + push!(all_times[tt], n) + else + push!(all_times, tt=>[n]) + end + end + end + end + return collect(keys(all_times)), collect(values(all_times)) +end +=# +#neurons = convert(Vector{Int64},population_code) + + +#stimulus_spikes_dist_neurons = cached_spikes[6][:] +#neurons = convert(Vector{Int64},stimulus_spikes_dist_neurons) + +#times = cached_spikes[3][:] + +#inputs = SpikeTime(time_and_offset,neurons) +#stim_size = length(unique(stimulus_spikes_dist_neurons)) +#display(plot(scatter(neurons,times))) +#stparam = SpikeTimeStimulusParameter(times,stimulus_spikes_dist_neurons) +#SpikeTimeStimulus(stim_size,neurons,param=stparam) + +#= +Extreme levels of fatigue excerbated by medication. + +ΔTs = -100:1:100ms +ΔWs = zeros(Float32, length(ΔTs)) +Threads.@threads for i in eachindex(ΔTs) + ΔT = ΔTs[i] + spiketime = [2000ms, 2000ms+ΔT] + + neurons = [[1], [2]] + inputs = SpikeTime(spiketime, neurons) + w = zeros(Float32, 2,2) + w[1, 2] = 1f0 + st = Identity(N=max_neurons(inputs)) + stim = SpikeTimeStimulusIdentity(st, :g, param=inputs) + syn = SpikingSynapse( st, st, nothing, w = w, param = stdp_param) + model = merge_models(pop=st, stim=stim, syn=syn, silent=true) + SNN.monitor(model.pop..., [:fire]) + SNN.monitor(model.syn..., [:tpre, :tpost]) + train!(model=model, duration=3000ms, dt=0.1ms) + ΔWs[i] = model.syn[1].W[1] - 1 +end +=# + +#SpikingNeuralNetworks.SpikeTimeStimulus +#@show(help(SpikingNeuralNetworks.stimulate)) From d0779e93639a1106681fb63e8b3fa8df213fcbca Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Thu, 5 Dec 2024 19:01:44 +1100 Subject: [PATCH 07/43] differencing and cleanup --- examples/Tonic_NMNIST_Stimulus.jl | 2 +- examples/getNMNIST.jl | 116 ---------- examples/potjans_nmnist.jl | 363 ++++++------------------------ 3 files changed, 64 insertions(+), 417 deletions(-) delete mode 100644 examples/getNMNIST.jl diff --git a/examples/Tonic_NMNIST_Stimulus.jl b/examples/Tonic_NMNIST_Stimulus.jl index 0e1c61f..b339ece 100644 --- a/examples/Tonic_NMNIST_Stimulus.jl +++ b/examples/Tonic_NMNIST_Stimulus.jl @@ -26,7 +26,7 @@ module Tonic_NMNIST_Stimulus l = convert(Int32,l) x = Vector{Int32}([e[1] for e in events]) y = Vector{Int32}([e[2] for e in events]) - ts = Vector{Float32}([e[3]/10000.0 for e in events]) + ts = Vector{Float32}([e[3]/100000.0 for e in events]) p = Vector{Int8}([e[4] for e in events]) for (x_,y_) in zip(x,y) push!(pop_stimulation,Int32(I[CartesianIndex(convert(Int32,x_+1),convert(Int32,y_+1))])) diff --git a/examples/getNMNIST.jl b/examples/getNMNIST.jl deleted file mode 100644 index ceb9191..0000000 --- a/examples/getNMNIST.jl +++ /dev/null @@ -1,116 +0,0 @@ -using JLD2 -using ProgressMeter -using PyCall -using Random - - - -function build_data_set_native(events,cnt,input_shape)#,l_change_cnt,l_old) - xx = Vector{Int32}([]) - yy = Vector{Int32}([]) - tts = Vector{Float32}([]) - polarity = Vector{Int8}([]) - label = Vector{Int32}([]) - #A = zeros(input_shape[1]+1,input_shape[2]+1,input_shape[3]+1) - A = zeros((400,400)) - I = LinearIndices(A) - pop_stimulation= Vector{Int32}([])#Vector{UInt32}([]) - @inbounds for (ind_,ev) in enumerate(events) - cnt+=1 - (x,y,ts,p,l) = ev - #@show(x,y,ts,p,l) - #rows, cols = size(pop_stimulation) - #if 1 <= x <= rows && 1 <= y <= cols - #index = Int32(I[CartesianIndex(Int(x), Int(y))]) - #if 1 <= x && 1 <= y - #if 1 <= x && 1 <= y - push!(pop_stimulation,Int32(I[CartesianIndex(convert(Int32,x),convert(Int32,y))])) - - #else - # @warn "Index out of bounds: x=$x, y=$y" - #end - - #push!(pop_stimulation,Int32(I[CartesianIndex(convert(Int32,x),convert(Int32,y))])) - push!(xx,convert(Int32,x)) - push!(yy,convert(Int32,y)) - ts = Float32(convert(Float32,ts)/1000.0) - push!(tts,ts) - push!(polarity,convert(Int8,p)) - l = convert(Int32,l) - push!(label,l) - end - spikes_typed::Tuple{Vector{Int32}, Vector{Int32}, Vector{Float32}, Vector{Int8}, Vector{Int32},Vector{Any}} = (xx,yy,tts,polarity,label,pop_stimulation) - (cnt,spikes_typed)#,l_change_cnt,l_old) -end - -function getNMNIST() - - pushfirst!(PyVector(pyimport("sys")."path"), "") - nmnist_module = pyimport("batch_nmnist_motions") - dataset::PyObject = nmnist_module.NMNIST("./") - training_order = shuffle(0:dataset.get_count()-1) - #cached_spikes::Array{Tuple{Vector{UInt32}, Vector{UInt32}, Vector{Float32}, Vector{Int8}, Vector{UInt32}, Vector{Any}}} = [] - cached_spikes = [] - input_shape = dataset.get_element_dimensions() - cnt = 0 - @time @inbounds for batch in 1:100:length(training_order) - if cnt<400 - - events = dataset.get_dataset_item(training_order[batch:batch+100-1]) - cnt,batch_nmnist = build_data_set_native(events,cnt,input_shape) - push!(cached_spikes,batch_nmnist) - end - end - @save "partial_mnmist.jld" cached_spikes - cached_spikes -end - - -function expected_spike_format(empty_spike_cont,nodes1,times1,maxt) - nodes1 = [i+1 for i in nodes1] - - @inbounds for i in collect(1:1220) - @inbounds for (neuron, t) in zip(nodes1,times1) - if i == neuron - push!(empty_spike_cont[Int32(i)],Float32(t)+Float32(maxt)) - end - end - end - empty_spike_cont,minimum(empty_spike_cont),maximum(empty_spike_cont) -end - -function NMNIST_pre_process_spike_data(temp_container_store;duration=25) - spike_packet_lists = Vector{Any}([]) - labelsl = Vector{Any}([]) - packet_window_boundaries = Vector{Any}([]) - maxt = 0 - empty_spike_cont = [] - @inbounds for i in collect(1:1220) - push!(empty_spike_cont,[]) - end - cnt = 0 - @inbounds @showprogress for (ind,s) in enumerate(temp_container_store) - (times,labels,nodes) = (s[1],s[2],s[3]) - maxt = maximum(times) - if length(times) != 0 - if cnt 1000, "z_dimension"=> 1000, @@ -194,14 +85,15 @@ function potjans_conn(Ne) # Have the thicknesses proportional to the numbers of E cells in each layer "layer_thicknesses"=> Dict( - "L23"=> total_cortical_thickness*N_full[0]/N_E_total, - "L4" => total_cortical_thickness*N_full[2]/N_E_total, - "L5" => total_cortical_thickness*N_full[4]/N_E_total, - "L6" => total_cortical_thickness*N_full[6]/N_E_total, + "L23"=> total_cortical_thickness*N_full[1]/N_E_total, + "L4" => total_cortical_thickness*N_full[3]/N_E_total, + "L5" => total_cortical_thickness*N_full[5]/N_E_total, + "L6" => total_cortical_thickness*N_full[7]/N_E_total, "thalamus" => 100 ) ) - net_dict = Dict( + + net_dict = Dict{String, Any}( "PSP_e"=> 0.15, # Relative standard deviation of the postsynaptic potential. "PSP_sd"=> 0.1, @@ -210,7 +102,7 @@ function potjans_conn(Ne) # Rate of the Poissonian spike generator (in Hz). "bg_rate"=> 8., # Turn Poisson input on or off (True or False). - "poisson_input"=> True, + "poisson_input"=> true, # Delay of the Poisson generator (in ms). "poisson_delay"=> 1.5, # Mean delay of excitatory connections (in ms). @@ -219,31 +111,8 @@ function potjans_conn(Ne) "mean_delay_inh"=> 0.75, # Relative standard deviation of the delay of excitatory and # inhibitory connections (in relative units). - "rel_std_delay"=> 0.5, - ) - updated_dict = Dict( - # PSP mean matrix. - "PSP_mean_matrix"=> get_mean_PSP_matrix( - net_dict['PSP_e'], net_dict['g'], len(layer_names) - ), - # PSP std matrix. - "PSP_std_matrix"=> get_std_PSP_matrix( - net_dict['PSP_sd'], len(layer_names) - ), - # mean delay matrix. - "mean_delay_matrix"=> get_mean_delays( - net_dict['mean_delay_exc'], net_dict['mean_delay_inh'], - len(layer_names) - ), - # std delay matrix. - "std_delay_matrix"=> get_std_delays( - net_dict['mean_delay_exc'] * net_dict['rel_std_delay'], - net_dict['mean_delay_inh'] * net_dict['rel_std_delay'], - len(layer_names]) - ), + "rel_std_delay"=> 0.5 ) - merge!(net_dict, updated_dict) - # Replace static matrix with a regular matrix for `conn_probs` # ! the convention is j_post_pre. This is how the matrices `w` are built. Are you using that when defining the parameters? conn_probs = Float32[ @@ -257,36 +126,16 @@ function potjans_conn(Ne) 0.0364 0.001 0.0034 0.0005 0.0277 0.008 0.0658 0.1443 ] - # !Assign dimensions to these parameters, and some reference - pree = 0.1 - g = 1.0 - tau_meme = 10.0 # (ms) - # Synaptic strengths for each connection type - #= - K = round(Int, Ne * pree) - sqrtK = sqrt(K) - - # !Same, the convention is j_post_pre - je = 2.0 / sqrtK * tau_meme * g - ji = 2.0 / sqrtK * tau_meme * g - jee = 0.15 * je - jie = je - jei = -0.75 * ji - jii = -ji - =# - - jii = jie = -4.0 - jee = jei = 0.15 - g_strengths = dict2ntuple(SNN.@symdict jee jie jei jii) - + conn_j = zeros(Float32, size(conn_probs)) for pre in eachindex(layer_names) for post in eachindex(layer_names) - conn_j[post, pre ] = j_from_name(layer_names[pre], layer_names[post], g_strengths) + + conn_j[post, pre ] = j_from_name(layer_names[pre], layer_names[post]) + end end - - return layer_names, conn_probs, conn_j + return layer_names, conn_probs, conn_j,net_dict end @@ -301,11 +150,18 @@ function potjans_layer(scale) inh_pop = filter(x -> occursin("I", String(x)), keys(neurons)) Ne = trunc(Int32, sum([neurons[k].N for k in exc_pop])) Ni = trunc(Int32, sum([neurons[k].N for k in inh_pop])) - @show exc_pop, Ne, Ni - layer_names, conn_probs, conn_j = potjans_conn(Ne) + layer_names, conn_probs, conn_j,net_dict = potjans_conn(Ne) + syn_weight_dist = Normal(0.15, 0.1) + delay_dist_exc = Normal(1.5, 0.5) + delay_dist_inh = Normal( 0.75, 0.5) ## Create the synaptic connections based on the connection probabilities and synaptic weights assigned to each pre-post pair connections = Dict() + stdp_param = STDPParameter(A_pre =5e-1, + A_post=-5e-1, + τpre =20ms, + τpost =15ms) + for i in eachindex(layer_names) for j in eachindex(layer_names) pre = layer_names[i] @@ -314,12 +170,16 @@ function potjans_layer(scale) J = conn_j[j, i] sym = J>=0 ? :ge : :gi μ = abs(J) - s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ, p=p, σ=0) + if J>=0 + s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ, p=p, σ=0,param=stdp_param, delay_dist=delay_dist_exc) + else + s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ, p=p, σ=0, delay_dist=delay_dist_inh) + + end connections[Symbol(string(pre,"_", post))] = s end end - #'full_mean_rates': full_mean_rates = [0.971, 2.868, 4.746, 5.396, 8.142, 9.078, 0.991, 7.523] stimuli = Dict() for (ind,pop) in enumerate(exc_pop) @@ -328,28 +188,24 @@ function potjans_layer(scale) s = SNN.PoissonStimulus(post, :ge; param = νe, cells=:ALL, μ=1.f0, name="PoissonE_$(post.name)") stimuli[Symbol(string("PoissonE_", pop))] = s end - return merge_models(neurons,connections, stimuli),neurons,connections,stimuli + return merge_models(neurons,connections, stimuli),neurons,connections,stimuli,net_dict end if !isfile("cached_potjans_model.jld2") - model,neurons_,connections_,stimuli_ = potjans_layer(0.125) - @save "cached_potjans_model.jld2" model neurons_ connections_ stimuli_ + model,neurons_,connections_,stimuli_,net_dict = potjans_layer(0.085) + @save "cached_potjans_model.jld2" model neurons_ connections_ stimuli_ net_dict else - @load "cached_potjans_model.jld2" model neurons_ connections_ stimuli_ + @load "cached_potjans_model.jld2" model neurons_ connections_ stimuli_ net_dict end + duration = 15000ms SNN.monitor([model.pop...], [:fire]) SNN.monitor([model.pop...], [:v], sr=200Hz) SNN.sim!(model=model; duration = duration, pbar = true, dt = 0.125) -display(SNN.raster(model.pop, [0s, 15s])) +display(SNN.raster(model.pop, [1.75s, 2s])) savefig("without_stimulus.png") - - - - training_order = shuffle(0:60000-1) - cached_spikes=[] for i in 1:40 push!(cached_spikes,Tonic_NMNIST_Stimulus.getNMNIST(training_order[i])) @@ -378,13 +234,13 @@ end p1 = Plots.scatter() scatter!(p1, -time_and_offset, -population_code, -ms = 1, # Marker size -ylabel = "Time (ms)", -xlabel = "Neuron Index", -title = "Spiking Activity with Distinct Characters", -legend=false + time_and_offset, + population_code, + ms = 0.5, # Marker size + ylabel = "Neuron Index" , + xlabel ="Time (ms)", + title = "Spiking Activity with Distinct Characters", + legend=false ) display(plot(p1)) savefig("stimulus.png") @@ -398,19 +254,20 @@ w = ones(Float32,neurons_[:E4].N,max_neurons(inputs))*15 st = Identity(N=max_neurons(inputs)) stim = SpikeTimeStimulusIdentity(st, :g, param=inputs) -stdp_param = STDPParameter(A_pre =5e-1, - A_post=-5e-1, - τpre =20ms, - τpost =15ms) -syn = SpikingSynapse( st, neurons_[:E4], nothing, w = w, param = stdp_param) + +#s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ, p=p, σ=0) + +syn = SpikingSynapse( st, neurons_[:E4], nothing, w = w)#, param = stdp_param) model2 = merge_models(pop=[st,model], stim=[stim,stimuli_], syn=[syn,connections_], silent=false) duration = 15000ms SNN.monitor([model2.pop...], [:fire]) SNN.monitor([model2.pop...], [:v], sr=200Hz) SNN.sim!(model=model2; duration = duration, pbar = true, dt = 0.125) -display(SNN.raster(model2.pop, [0s, 15s])) +#display(SNN.raster(model2.pop, [0s, 15s])) +display(SNN.raster(model2.pop, [1.75s, 2s])) + savefig("with_stimulus.png") Trange = 0:10:15s @@ -426,97 +283,3 @@ layer_names, conn_probs, conn_j = potjans_conn(4000) pj = heatmap(conn_j, xticks=(1:8,layer_names), yticks=(1:8,layer_names), aspect_ratio=1, color=:bluesreds, title="Synaptic weights", xlabel="Presynaptic", ylabel="Postsynaptic", size=(500,500), clims=(-maximum(abs.(conn_j)), maximum(abs.(conn_j)))) pprob=heatmap(conn_probs, xticks=(1:8,layer_names), yticks=(1:8,layer_names), aspect_ratio=1, color=:viridis, title="Connection probability", xlabel="Presynaptic", ylabel="Postsynaptic", size=(500,500)) plot(pprob, pj, layout=(1,2), size=(1000,500), margin=5Plots.mm) - -## - - #Tonic_NMNIST_Stimulus.spike_chara - #labels = cached_spikes[5][:] - #unique_labels = unique(labels) - #labeled_packets = Dict() - #for filter_label in unique_labels - # population_code,time_and_offset = Tonic_NMNIST_Stimulus.spike_packets_by_labels(filter_label,cached_spikes) - # labeled_packets[filter_label] = (population_code,time_and_offset) - # Tonic_NMNIST_Stimulus.spike_character_plot(filter_label,population_code,time_and_offset) - - #end - - #= - labels = cached_spikes[5][:] - unique_labels = unique(labels) - labeled_packets = Dict() - for filter_label in unique_labels - population_code,time_and_offset = Tonic_NMNIST_Stimulus.spike_packets_by_labels(filter_label,cached_spikes) - labeled_packets[filter_label] = (population_code,time_and_offset) - Tonic_NMNIST_Stimulus.spike_character_plot(filter_label,population_code,time_and_offset) - - end - =# - #scatter_plot(filter_label,population_code,time_and_offset) - - - -#array_size = maximum(unique_neurons) -#w = zeros(Float32, array_size,array_size) - -#w[population_code, population_code] = 1f0 - -#stparam = SpikeTimeStimulusParameter(time_and_offset,population_code) -#SpikeTimeStimulus(stim_size,neurons,param=stparam) -#= -function times_x_neurons(spikes::Spiketimes) - all_times = Dict{Float64, Vector{Int}}() - for n in eachindex(spikes) - if !isempty(spikes[n]) - for tt in spikes[n] - # tt = round(Int,t*1000/dt) ## from seconds to timesteps - if haskey(all_times,tt) - push!(all_times[tt], n) - else - push!(all_times, tt=>[n]) - end - end - end - end - return collect(keys(all_times)), collect(values(all_times)) -end -=# -#neurons = convert(Vector{Int64},population_code) - - -#stimulus_spikes_dist_neurons = cached_spikes[6][:] -#neurons = convert(Vector{Int64},stimulus_spikes_dist_neurons) - -#times = cached_spikes[3][:] - -#inputs = SpikeTime(time_and_offset,neurons) -#stim_size = length(unique(stimulus_spikes_dist_neurons)) -#display(plot(scatter(neurons,times))) -#stparam = SpikeTimeStimulusParameter(times,stimulus_spikes_dist_neurons) -#SpikeTimeStimulus(stim_size,neurons,param=stparam) - -#= -Extreme levels of fatigue excerbated by medication. - -ΔTs = -100:1:100ms -ΔWs = zeros(Float32, length(ΔTs)) -Threads.@threads for i in eachindex(ΔTs) - ΔT = ΔTs[i] - spiketime = [2000ms, 2000ms+ΔT] - - neurons = [[1], [2]] - inputs = SpikeTime(spiketime, neurons) - w = zeros(Float32, 2,2) - w[1, 2] = 1f0 - st = Identity(N=max_neurons(inputs)) - stim = SpikeTimeStimulusIdentity(st, :g, param=inputs) - syn = SpikingSynapse( st, st, nothing, w = w, param = stdp_param) - model = merge_models(pop=st, stim=stim, syn=syn, silent=true) - SNN.monitor(model.pop..., [:fire]) - SNN.monitor(model.syn..., [:tpre, :tpost]) - train!(model=model, duration=3000ms, dt=0.1ms) - ΔWs[i] = model.syn[1].W[1] - 1 -end -=# - -#SpikingNeuralNetworks.SpikeTimeStimulus -#@show(help(SpikingNeuralNetworks.stimulate)) From d4b874fe473a45fc953a882adb1e57cb8f4e6d93 Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Thu, 5 Dec 2024 19:07:59 +1100 Subject: [PATCH 08/43] revised batch_nmnist.py --- examples/batch_nmnist_motions.py | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/examples/batch_nmnist_motions.py b/examples/batch_nmnist_motions.py index a999c8e..cd17492 100644 --- a/examples/batch_nmnist_motions.py +++ b/examples/batch_nmnist_motions.py @@ -13,7 +13,7 @@ #import h5py import numpy as np import tonic -from numpy.lib.recfunctions import merge_arrays +#from numpy.lib.recfunctions import merge_arrays class NMNIST(object): @@ -28,36 +28,21 @@ def __init__(self, dataset_path, train=True, first_saccade_only=False,transform= raise Exception("Specified dataset path does not exist") self._dataset = tonic.datasets.NMNIST(save_to='./data', train=train, first_saccade_only=first_saccade_only,transform=transform) # self.transform = transform - + """ def get_dataset_item(self, indices): assert(len(indices) <= 100) all_events = [] for id,index in enumerate(indices): - #(grid_x,grid_y) = np.unravel_index(id,(10,10)) - events, label = self._dataset[index] - #print(events) label_array = np.full(events['x'].shape[0],label,dtype=[('label','i8')]) event_array = merge_arrays((events,label_array),flatten=True) - ## - # Its possible that the 36 is depreciated. - ## - #event_array['x'] = grid_x + event_array['x'] + 1 - #event_array['y'] = grid_y + event_array['y'] + 1 - #event_array['x'] = grid_x*36 + event_array['x'] + 1 - #event_array['y'] = grid_y*36 + event_array['y'] + 1 - - # event_array[:,3] -= event_array[0,3] all_events.append(event_array) super_events = np.hstack(all_events) super_events = super_events[super_events['t'].argsort()] - - return super_events - def get_count(self): """ Returns the number of items """ @@ -69,4 +54,5 @@ def get_label_range(self): def get_element_dimensions(self): """ Returns a tuple containing the dimensions of each image in the dataset """ - return self._dataset.sensor_size \ No newline at end of file + return self._dataset.sensor_size + """ \ No newline at end of file From 0929dcb036936469a045d3ccd707818478a1a314 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Fri, 6 Dec 2024 20:53:22 +1100 Subject: [PATCH 09/43] Create ci.yml --- .github/workflows/ci.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..833cea5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI - Julia + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Julia + uses: julia-actions/setup-julia@v1 + with: + version: '1.9' # Adjust to your preferred Julia version + + - name: Install Dependencies + run: julia --project=. -e 'using Pkg;Pkg.instantiate()' + + - name: Run tests + run: julia --project=@. -e 'using Pkg; Pkg.test()' + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v3 + with: + name: test-results + path: test/ From 069a9cf8f7df69f0108c595601f417d25566d0c7 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Fri, 6 Dec 2024 20:55:29 +1100 Subject: [PATCH 10/43] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5e80531..fdb5309 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # SpikingNeuralNetworks +![CI](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg) ## Installation From fbc4fc6b3d2d76857eba7e8d85040cf931c236c1 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:22:49 +0000 Subject: [PATCH 11/43] CompatHelper: add new compat entry for StaticArrays at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..511f101 100644 --- a/Project.toml +++ b/Project.toml @@ -41,6 +41,7 @@ Interpolations = "0.15.1" MetaGraphs = "0.8.0" Requires = "0.5, 1" SGtSNEpi = "0.4.0" +StaticArrays = "1" StatsBase = "0.34.3" UnPack = "1" julia = "1" From 0c48350f46a432c42ebc27319f534cff96bcdb89 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:22:54 +0000 Subject: [PATCH 12/43] CompatHelper: add new compat entry for DrWatson at version 2, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..b616ba5 100644 --- a/Project.toml +++ b/Project.toml @@ -36,6 +36,7 @@ UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" CairoMakie = "0.12.16" Colors = "0.12.11" Distributions = "0.25.112" +DrWatson = "2" Graphs = "1.12.0" Interpolations = "0.15.1" MetaGraphs = "0.8.0" From 7c714793b3fde636d29d7fc66b955ec00caf0ac6 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:22:56 +0000 Subject: [PATCH 13/43] CompatHelper: add new compat entry for TimerOutputs at version 0.5, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..90a7bc5 100644 --- a/Project.toml +++ b/Project.toml @@ -42,6 +42,7 @@ MetaGraphs = "0.8.0" Requires = "0.5, 1" SGtSNEpi = "0.4.0" StatsBase = "0.34.3" +TimerOutputs = "0.5" UnPack = "1" julia = "1" From 3de60036cf97c2aa518db40a2ab5f39ac59edc63 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:22:58 +0000 Subject: [PATCH 14/43] CompatHelper: add new compat entry for RollingFunctions at version 0.8, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..0c35f61 100644 --- a/Project.toml +++ b/Project.toml @@ -40,6 +40,7 @@ Graphs = "1.12.0" Interpolations = "0.15.1" MetaGraphs = "0.8.0" Requires = "0.5, 1" +RollingFunctions = "0.8" SGtSNEpi = "0.4.0" StatsBase = "0.34.3" UnPack = "1" From c28cb903c38342afa86a55c57b36abc44a5e5dbb Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:00 +0000 Subject: [PATCH 15/43] CompatHelper: add new compat entry for Parameters at version 0.12, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..4b92c58 100644 --- a/Project.toml +++ b/Project.toml @@ -39,6 +39,7 @@ Distributions = "0.25.112" Graphs = "1.12.0" Interpolations = "0.15.1" MetaGraphs = "0.8.0" +Parameters = "0.12" Requires = "0.5, 1" SGtSNEpi = "0.4.0" StatsBase = "0.34.3" From 6684c51febc43d77dc3610218b33b8dc223174f5 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:02 +0000 Subject: [PATCH 16/43] CompatHelper: add new compat entry for Revise at version 3, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..613658e 100644 --- a/Project.toml +++ b/Project.toml @@ -40,6 +40,7 @@ Graphs = "1.12.0" Interpolations = "0.15.1" MetaGraphs = "0.8.0" Requires = "0.5, 1" +Revise = "3" SGtSNEpi = "0.4.0" StatsBase = "0.34.3" UnPack = "1" From 9d90d5d84700f45bbd1cc0a9621020d1bd1e7bd7 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:04 +0000 Subject: [PATCH 17/43] CompatHelper: add new compat entry for LoopVectorization at version 0.12, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..0fc05c6 100644 --- a/Project.toml +++ b/Project.toml @@ -38,6 +38,7 @@ Colors = "0.12.11" Distributions = "0.25.112" Graphs = "1.12.0" Interpolations = "0.15.1" +LoopVectorization = "0.12" MetaGraphs = "0.8.0" Requires = "0.5, 1" SGtSNEpi = "0.4.0" From 47ff15540b7d27535dff7cb9265a16bca7ffbda4 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:06 +0000 Subject: [PATCH 18/43] CompatHelper: add new compat entry for BenchmarkTools at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..c9605ac 100644 --- a/Project.toml +++ b/Project.toml @@ -33,6 +33,7 @@ TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" [compat] +BenchmarkTools = "1" CairoMakie = "0.12.16" Colors = "0.12.11" Distributions = "0.25.112" From 89157d2109e7c4777de6ab9327973e57a2986e89 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:08 +0000 Subject: [PATCH 19/43] CompatHelper: add new compat entry for ProgressBars at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..233c24f 100644 --- a/Project.toml +++ b/Project.toml @@ -39,6 +39,7 @@ Distributions = "0.25.112" Graphs = "1.12.0" Interpolations = "0.15.1" MetaGraphs = "0.8.0" +ProgressBars = "1" Requires = "0.5, 1" SGtSNEpi = "0.4.0" StatsBase = "0.34.3" From b581d0057d0c0ba25571442253e18ac576e755f8 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:10 +0000 Subject: [PATCH 20/43] CompatHelper: bump compat for Colors to 0.13, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 193f0c0..c35b448 100644 --- a/Project.toml +++ b/Project.toml @@ -34,7 +34,7 @@ UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" [compat] CairoMakie = "0.12.16" -Colors = "0.12.11" +Colors = "0.12.11, 0.13" Distributions = "0.25.112" Graphs = "1.12.0" Interpolations = "0.15.1" From 4a54e3701de5d5501b860189ae2fe5b0f6eee4c1 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:12 +0000 Subject: [PATCH 21/43] CompatHelper: add new compat entry for Statistics at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..dd45aa6 100644 --- a/Project.toml +++ b/Project.toml @@ -41,6 +41,7 @@ Interpolations = "0.15.1" MetaGraphs = "0.8.0" Requires = "0.5, 1" SGtSNEpi = "0.4.0" +Statistics = "1" StatsBase = "0.34.3" UnPack = "1" julia = "1" From dd4b9dba3b38cdfb6ae22cf8e424b3e86c282793 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:14 +0000 Subject: [PATCH 22/43] CompatHelper: add new compat entry for ThreadTools at version 0.2, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..3d2b68a 100644 --- a/Project.toml +++ b/Project.toml @@ -42,6 +42,7 @@ MetaGraphs = "0.8.0" Requires = "0.5, 1" SGtSNEpi = "0.4.0" StatsBase = "0.34.3" +ThreadTools = "0.2" UnPack = "1" julia = "1" From 63f6179db12ee67109fe0d0722cddfd03a610f50 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:16 +0000 Subject: [PATCH 23/43] CompatHelper: add new compat entry for Dagger at version 0.18, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..2164bce 100644 --- a/Project.toml +++ b/Project.toml @@ -35,6 +35,7 @@ UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" [compat] CairoMakie = "0.12.16" Colors = "0.12.11" +Dagger = "0.18" Distributions = "0.25.112" Graphs = "1.12.0" Interpolations = "0.15.1" From b36927e20be548cb81be2d057b7aa799ebf2bf25 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 7 Dec 2024 01:23:19 +0000 Subject: [PATCH 24/43] CompatHelper: add new compat entry for Plots at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 193f0c0..db71bec 100644 --- a/Project.toml +++ b/Project.toml @@ -39,6 +39,7 @@ Distributions = "0.25.112" Graphs = "1.12.0" Interpolations = "0.15.1" MetaGraphs = "0.8.0" +Plots = "1" Requires = "0.5, 1" SGtSNEpi = "0.4.0" StatsBase = "0.34.3" From 4c3f4d7009a3e009185300b490d0080879023d16 Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Tue, 10 Dec 2024 13:24:30 +1100 Subject: [PATCH 25/43] Update code to reflect STDP learning on NMNIST --- .github/workflows/ci.yml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b46288a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI - Julia + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Julia + uses: julia-actions/setup-julia@v1 + with: + version: '1.9' # Adjust to your preferred Julia version + + - name: Install Dependencies + run: julia --project=. -e ' + using Pkg; + Pkg.add(PackageSpec(url="https://github.com/JuliaSNN/SpikingNeuralNetworks.jl")); + Pkg.instantiate() + ' + + - name: Run tests + run: julia --project=@. -e 'using Pkg; Pkg.test()' + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v3 + with: + name: test-results + path: test/ From 724bae1c9d90ace49e40dd37d3f240f6131997fa Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Fri, 13 Dec 2024 16:50:26 +1100 Subject: [PATCH 26/43] update code before rebase --- examples/paradoxical_effect.jl | 2 + examples/potjans_nmnist.jl | 100 ++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/examples/paradoxical_effect.jl b/examples/paradoxical_effect.jl index 722388d..e7d3540 100644 --- a/examples/paradoxical_effect.jl +++ b/examples/paradoxical_effect.jl @@ -67,3 +67,5 @@ plot!( legend = :topright, ) ## +inputs = SNN.Poisson(; N = 350, param = SNN.PoissonParameter(rate = 10.5Hz)) +@show(inputs) \ No newline at end of file diff --git a/examples/potjans_nmnist.jl b/examples/potjans_nmnist.jl index 74f6795..57320e0 100644 --- a/examples/potjans_nmnist.jl +++ b/examples/potjans_nmnist.jl @@ -173,7 +173,7 @@ function potjans_layer(scale) if J>=0 s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ, p=p, σ=0,param=stdp_param, delay_dist=delay_dist_exc) else - s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ, p=p, σ=0, delay_dist=delay_dist_inh) + s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = -μ, p=p, σ=0, delay_dist=delay_dist_inh) end connections[Symbol(string(pre,"_", post))] = s @@ -191,20 +191,91 @@ function potjans_layer(scale) return merge_models(neurons,connections, stimuli),neurons,connections,stimuli,net_dict end -if !isfile("cached_potjans_model.jld2") +#if !isfile("cached_potjans_model.jld2") model,neurons_,connections_,stimuli_,net_dict = potjans_layer(0.085) @save "cached_potjans_model.jld2" model neurons_ connections_ stimuli_ net_dict -else +#else @load "cached_potjans_model.jld2" model neurons_ connections_ stimuli_ net_dict +#end +#@show(connections_.vals) + +before_learnning_weights = model.syn[1].W +#= +ΔTs = -100:1:100ms +ΔWs = zeros(Float32, length(ΔTs)) +Threads.@threads for i in eachindex(ΔTs) + ΔT = ΔTs[i] + #spiketime = [2000ms, 2000ms+ΔT] + #neurons = [[1], [2]] + #inputs = SpikeTime(spiketime, neurons) + w = zeros(Float32, 2,2) + w[1, 2] = 1f0 + st = Identity(N=max_neurons(inputs)) + stim = SpikeTimeStimulusIdentity(st, :g, param=inputs) + syn = SpikingSynapse( st, st, nothing, w = w, param = stdp_param) + + model = merge_models(pop=st, stim=stim, syn=syn, silent=true) + SNN.monitor(model.pop..., [:fire]) + SNN.monitor(model.syn..., [:tpre, :tpost]) + train!(model=model, duration=3000ms, dt=0.1ms) + ΔWs[i] = model.syn[1].W[1] - 1 end +=# duration = 15000ms SNN.monitor([model.pop...], [:fire]) -SNN.monitor([model.pop...], [:v], sr=200Hz) -SNN.sim!(model=model; duration = duration, pbar = true, dt = 0.125) -display(SNN.raster(model.pop, [1.75s, 2s])) +#SNN.monitor([model.pop...], [:v], sr=200Hz) +SNN.sim!(model=model, duration=3000ms, dt=0.125, pbar = true)#, dt = 0.125) + +#= +# Example data: Spike times and trial start times +s#pike_times = [0.1, 0.15, 0.2, 0.4, 0.6, 0.65, 0.8, 1.0, 1.1, 1.3, 1.5] +t#rial_starts = [0.0, 1.0, 2.0] # Start times of each trial +num_trials = length(trial_starts) + +# Parameters +bin_width = 0.1 # Bin width in seconds +time_window = (0.0, 1.0) # Time window relative to each trial start + +# Create bins +edges = collect(time_window[1]:bin_width:time_window[2]) +num_bins = length(edges) - 1 + +# Initialize a matrix to hold spike counts +spike_counts = zeros(num_trials, num_bins) + +# Bin spikes for each trial +for (i, trial_start) in enumerate(trial_starts) + aligned_spikes = spike_times .- trial_start # Align spikes to trial start + filtered_spikes = aligned_spikes[aligned_spikes .>= time_window[1] .& aligned_spikes .< time_window[2]] + spike_counts[i, :] = histcounts(filtered_spikes, edges) +end + +# Compute average spike activity (optional) +average_spike_activity = mean(spike_counts, dims=1) + +# Plot heatmap +heatmap( + edges[1:end-1] .+ bin_width / 2, # Bin centers + 1:num_trials, # Trial indices + spike_counts, + xlabel="Time (s)", + ylabel="Trial", + color=:viridis, + title="Spike Activity Heatmap" +) +=# +SNN.train!(model=model, duration=3000ms, dt=0.125, pbar = true)#, dt = 0.125) + +#SNN.sim!(model=model; duration = duration, pbar = true, dt = 0.125) +display(SNN.raster(model.pop, [1.0s, 2s])) savefig("without_stimulus.png") + + + +after_learnning_weights0 = model.syn[1].W + training_order = shuffle(0:60000-1) cached_spikes=[] for i in 1:40 @@ -256,16 +327,27 @@ st = Identity(N=max_neurons(inputs)) stim = SpikeTimeStimulusIdentity(st, :g, param=inputs) -#s = SNN.SpikingSynapse(neurons[pre], neurons[post], sym; μ = μ, p=p, σ=0) - syn = SpikingSynapse( st, neurons_[:E4], nothing, w = w)#, param = stdp_param) model2 = merge_models(pop=[st,model], stim=[stim,stimuli_], syn=[syn,connections_], silent=false) duration = 15000ms SNN.monitor([model2.pop...], [:fire]) SNN.monitor([model2.pop...], [:v], sr=200Hz) -SNN.sim!(model=model2; duration = duration, pbar = true, dt = 0.125) +SNN.train!(model=model2; duration = duration, pbar = true, dt = 0.125) #display(SNN.raster(model2.pop, [0s, 15s])) + +after_learnning_weights1 = model.syn[1].W + +@show(mean(before_learnning_weights)) +@show(mean(after_learnning_weights0)) +@show(mean(after_learnning_weights1)) + +#mean(model2.syn[1].W) + +SNN.spiketimes(model.pop[1]) + +#x, y, y0 = SNN._raster(model2.pop.pop_2_E5,[1.95s, 2s])) + display(SNN.raster(model2.pop, [1.75s, 2s])) savefig("with_stimulus.png") From 3160b2718295f4399bbf2f88f62159c0f7b4907c Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Sat, 11 Jan 2025 13:54:23 +1100 Subject: [PATCH 27/43] update test file for remote --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 26702d3..13d9eae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,7 +19,7 @@ include("stdp_demo.jl") include("tripod.jl") include("tripod_network.jl") include("poisson_stim.jl") -include("tripod.jl") +include("tripod_soma.jl") include("tripod_network.jl") include("spiketime.jl") include("ballandstick.jl") \ No newline at end of file From 8cc36db7587b9ed4f60d40f33884513c759d2d08 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Sat, 11 Jan 2025 14:12:01 +1100 Subject: [PATCH 28/43] Update runtests.jl --- test/runtests.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 13d9eae..e272aab 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -16,10 +16,10 @@ include("iz_neuron.jl") include("oja.jl") include("rate_net.jl") include("stdp_demo.jl") -include("tripod.jl") +#include("tripod.jl") include("tripod_network.jl") include("poisson_stim.jl") include("tripod_soma.jl") include("tripod_network.jl") include("spiketime.jl") -include("ballandstick.jl") \ No newline at end of file +include("ballandstick.jl") From fa4cc42aa35156109452475e0e3222430cc29493 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Sat, 11 Jan 2025 14:25:45 +1100 Subject: [PATCH 29/43] Update runtests.jl --- test/runtests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index e272aab..52010d8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -20,6 +20,6 @@ include("stdp_demo.jl") include("tripod_network.jl") include("poisson_stim.jl") include("tripod_soma.jl") -include("tripod_network.jl") -include("spiketime.jl") -include("ballandstick.jl") +#include("tripod_network.jl") +#include("spiketime.jl") +#include("ballandstick.jl") From 877f2d22cd3cfc366721e24439879d8cb08b7b41 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Sat, 11 Jan 2025 14:53:34 +1100 Subject: [PATCH 30/43] Update runtests.jl --- test/runtests.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 52010d8..e7dff7e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -16,10 +16,9 @@ include("iz_neuron.jl") include("oja.jl") include("rate_net.jl") include("stdp_demo.jl") -#include("tripod.jl") -include("tripod_network.jl") include("poisson_stim.jl") include("tripod_soma.jl") +#include("tripod.jl") #include("tripod_network.jl") #include("spiketime.jl") #include("ballandstick.jl") From 8af9942aa544cc990fb0437cd0d3dbdb70091eb3 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:52:49 +1100 Subject: [PATCH 31/43] Update runtests.jl --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index e7dff7e..470f2e7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,7 +17,7 @@ include("oja.jl") include("rate_net.jl") include("stdp_demo.jl") include("poisson_stim.jl") -include("tripod_soma.jl") +#include("tripod_soma.jl") #include("tripod.jl") #include("tripod_network.jl") #include("spiketime.jl") From 9f8bfdd7b8dd480b4db757c9a551f36066f343fc Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Sun, 12 Jan 2025 15:03:56 +1100 Subject: [PATCH 32/43] .gitignore change --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5a198a7..3853fc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .vscode Manifest.toml -docs/build \ No newline at end of file +docs/buildManifest.toml From 265c83835c257b5a0c63254d404aeef07fbe94a9 Mon Sep 17 00:00:00 2001 From: Russell Jarvis Date: Sun, 12 Jan 2025 15:04:41 +1100 Subject: [PATCH 33/43] .gitignore change --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 13d9eae..ff43797 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -16,7 +16,7 @@ include("iz_neuron.jl") include("oja.jl") include("rate_net.jl") include("stdp_demo.jl") -include("tripod.jl") +#include("tripod.jl") include("tripod_network.jl") include("poisson_stim.jl") include("tripod_soma.jl") From 0513e24cffee90ba96deb24fe39a26d1c95b9ab7 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:11:00 +1100 Subject: [PATCH 34/43] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3853fc6..5883008 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .vscode Manifest.toml -docs/buildManifest.toml +docs/build From 410a8a11ba35a299f581f1b3bec2a30984dbb9bf Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:16:47 +1100 Subject: [PATCH 35/43] Update CI.yml From 9ab2901ddbf9afed0bb2fd344d15af41ad699816 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Sun, 12 Jan 2025 16:11:02 +1100 Subject: [PATCH 36/43] Delete .github/workflows/ci.yml --- .github/workflows/ci.yml | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 833cea5..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CI - Julia - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Set up Julia - uses: julia-actions/setup-julia@v1 - with: - version: '1.9' # Adjust to your preferred Julia version - - - name: Install Dependencies - run: julia --project=. -e 'using Pkg;Pkg.instantiate()' - - - name: Run tests - run: julia --project=@. -e 'using Pkg; Pkg.test()' - - - name: Upload test results - if: failure() - uses: actions/upload-artifact@v3 - with: - name: test-results - path: test/ From fa770c1835138cef076b2998a24321ff109dd947 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:56:34 +1100 Subject: [PATCH 37/43] Update Project.toml --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 58c959b..7956d6d 100644 --- a/Project.toml +++ b/Project.toml @@ -32,6 +32,7 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" ThreadTools = "dbf13d8f-d36e-4350-8970-f3a99faba1a8" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] BenchmarkTools = "1" From 6ce45be5ab5592add6d8c92dbc5acbd024825438 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Tue, 14 Jan 2025 01:14:12 +0000 Subject: [PATCH 38/43] CompatHelper: bump compat for CairoMakie to 0.13, (keep existing compat) --- Project.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index 7956d6d..41dc554 100644 --- a/Project.toml +++ b/Project.toml @@ -36,28 +36,28 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] BenchmarkTools = "1" -CairoMakie = "0.12.16" +CairoMakie = "0.12.16, 0.13" Colors = "0.12.11, 0.13" Dagger = "0.18" Distributions = "0.25.112" -DrWatson = "2" Documenter = "1.8.0" +DrWatson = "2" Graphs = "1.12.0" Interpolations = "0.15.1" LoopVectorization = "0.12" MetaGraphs = "0.8.0" Parameters = "0.12" -ProgressBars = "1" Plots = "1" +ProgressBars = "1" Requires = "0.5, 1" -RollingFunctions = "0.8" Revise = "3" +RollingFunctions = "0.8" SGtSNEpi = "0.4.0" StaticArrays = "1" Statistics = "1" StatsBase = "0.34.3" -TimerOutputs = "0.5" ThreadTools = "0.2" +TimerOutputs = "0.5" UnPack = "1" julia = "1" From 694de8615f49938cfb2efa2f8d321b791e1a5b26 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Tue, 14 Jan 2025 01:14:17 +0000 Subject: [PATCH 39/43] CompatHelper: add new compat entry for Unitful at version 1, (keep existing compat) --- Project.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 7956d6d..6575cbd 100644 --- a/Project.toml +++ b/Project.toml @@ -40,25 +40,26 @@ CairoMakie = "0.12.16" Colors = "0.12.11, 0.13" Dagger = "0.18" Distributions = "0.25.112" -DrWatson = "2" Documenter = "1.8.0" +DrWatson = "2" Graphs = "1.12.0" Interpolations = "0.15.1" LoopVectorization = "0.12" MetaGraphs = "0.8.0" Parameters = "0.12" -ProgressBars = "1" Plots = "1" +ProgressBars = "1" Requires = "0.5, 1" -RollingFunctions = "0.8" Revise = "3" +RollingFunctions = "0.8" SGtSNEpi = "0.4.0" StaticArrays = "1" Statistics = "1" StatsBase = "0.34.3" -TimerOutputs = "0.5" ThreadTools = "0.2" +TimerOutputs = "0.5" UnPack = "1" +Unitful = "1" julia = "1" [extras] From be886712ef332f3be4b8b9d19338a0ac6b049ab1 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Thu, 30 Jan 2025 15:31:40 +1100 Subject: [PATCH 40/43] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fdb5309..512b20a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # SpikingNeuralNetworks -![CI](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg) +[![CI](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg)](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml) ## Installation From 529a1a56fb0840321888e159d567fe5197dabe8d Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Thu, 30 Jan 2025 15:32:34 +1100 Subject: [PATCH 41/43] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 512b20a..23e004f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # SpikingNeuralNetworks -[![CI](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg)](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml) +![CI](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg) + +#(https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml) ## Installation From b958f3b39a6a5a4a615cec9dfbef90efecf7fd1d Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Thu, 30 Jan 2025 15:33:03 +1100 Subject: [PATCH 42/43] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 23e004f..4165e67 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # SpikingNeuralNetworks -![CI](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg) +![](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg) -#(https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml) ## Installation From d1eedb51a1aae84f0049ff316613d8c7fd11bb64 Mon Sep 17 00:00:00 2001 From: Russell Jarvis <7786645+russelljjarvis@users.noreply.github.com> Date: Thu, 30 Jan 2025 15:35:04 +1100 Subject: [PATCH 43/43] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4165e67..72e3461 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # SpikingNeuralNetworks -![](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg) +![CI](https://github.com/JuliaNeuroscience/SpikingNeuralNetworks.jl/actions/workflows/ci.yml/badge.svg) + ## Installation