Skip to content

Commit e3f053a

Browse files
authored
Population updates (#62)
1 parent 269df28 commit e3f053a

File tree

7 files changed

+98
-83
lines changed

7 files changed

+98
-83
lines changed

demo/pinsky_network.jl

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
using LinearAlgebra, OrdinaryDiffEq, ModelingToolkit
2+
using LinearAlgebra, OrdinaryDiffEq, ModelingToolkit, Graphs
33
LinearAlgebra.BLAS.set_num_threads(6)
44

55
include(joinpath(@__DIR__, "traub_kinetics.jl"))
@@ -48,67 +48,63 @@ import Conductor: NMDA, AMPA
4848
[Gate(inv(1 + 0.28 * exp(-0.062(Vₘ - 60.0))); name = :e),
4949
Gate(S, [D(S) ~ -S/τNMDA]),
5050
Gate(inv(1 - p), name = :pnmda)],
51-
max_s = 0.027mS)
51+
max_s = 0.02mS)
5252

5353
@named AMPAChan = SynapticChannel(ConstantValueEvent(S; threshold = 20mV), AMPA,
5454
[Gate(S, [D(S) ~ -S/τAMPA]),
5555
Gate(inv(1 - p), name = :pampa)],
56-
max_s = 0.008mS)
56+
max_s = 0.04mS)
5757

5858
ENMDA = EquilibriumPotential(NMDA, 60mV, name = :NMDA)
5959
EAMPA = EquilibriumPotential(AMPA, 60mV, name = :AMPA)
6060
revmap = Dict([NMDAChan => ENMDA, AMPAChan => EAMPA])
6161

62-
# A single neuron was "briefly" stimulated to trigger the network
62+
# Create a neuron group
63+
n_neurons = 100
64+
@named neurons = Population(mcneuron, n_neurons);
65+
66+
# A brief stimulation to trigger the network
6367
@named I_pulse = PulseTrain(amplitude = 5.0µA / 0.5,
6468
duration = 50ms,
6569
delay = 150.0ms,
6670
offset = -0.5µA / 0.5)
6771

68-
soma_stimulated = Compartment(Vₘ, deepcopy(soma_dynamics);
69-
geometry = Unitless(0.5), # FIXME: should take p param
70-
capacitance = capacitance,
71-
stimuli = [I_pulse],
72-
name = :soma)
73-
mcstim_topology = MultiCompartmentTopology([soma_stimulated, dendrite]);
74-
add_junction!(mcstim_topology, soma_stimulated, dendrite, (gc_soma, gc_dendrite))
75-
@named mcneuron_stim = MultiCompartment(mcstim_topology)
76-
77-
# Need to introduce 10% gca variance as per Pinsky/Rinzel
78-
n_neurons = 100
79-
neuronpopulation = [Conductor.replicate(mcneuron) for _ in 1:n_neurons];
80-
neuronpopulation[4] = mcneuron_stim
81-
topology = NetworkTopology(neuronpopulation, [NMDAChan, AMPAChan]);
72+
# apply the stimulus to the root compartment of the 4th neuron
73+
add_stimuli!(neurons, [I_pulse], (4,1))
74+
topology = NetworkTopology(neurons, [NMDAChan, AMPAChan]);
8275

83-
using Graphs
76+
# Generate connectivity graphs
8477
nmda_g = random_regular_digraph(n_neurons, fld(n_neurons, 5), dir = :in)
8578
ampa_g = random_regular_digraph(n_neurons, fld(n_neurons, 5), dir = :in)
8679

87-
# We could allow users to supply a lambda/function to map in order to get this behavior
80+
# Use graphs to create synapses from soma (output) to dendrites (inputs) for neurons
8881
for (i, e) in enumerate(edges(nmda_g))
89-
add_synapse!(topology, neuronpopulation[src(e)].soma, neuronpopulation[dst(e)].dendrite,
82+
add_synapse!(topology, src(e), (dst(e),2),
9083
NMDAChan, 1.0)
9184
end
9285

9386
for (i, e) in enumerate(edges(ampa_g))
94-
add_synapse!(topology, neuronpopulation[src(e)].soma, neuronpopulation[dst(e)].dendrite,
87+
add_synapse!(topology, src(e), (dst(e),2),
9588
AMPAChan, 1.0)
9689
end
9790

9891
@named net = NeuronalNetworkSystem(topology, revmap);
9992
t_total = 2000.0
100-
simp = Simulation(net, t_total*ms, return_system = true)
10193
prob = Simulation(net, t_total*ms)
102-
sol = solve(prob, RK4(); adaptive = false, dt = 0.05);
94+
@time sol = solve(prob, RK4(); adaptive = false, dt = 0.05);
10395

96+
############################################################################################
10497
# Pinsky and Rinzel displayed their results as a plot of N neurons over 20mV
98+
using Statistics, Plots
99+
100+
# helper function
105101
indexof(sym, syms) = findfirst(isequal(sym), syms)
102+
103+
simp = ODESystem(net)
106104
dvs = states(simp)
107105
interpolated = sol(1:0.2:t_total,
108-
idxs = [indexof(x.soma.Vₘ, dvs) for x in neuronpopulation[5:end]])
106+
idxs = [indexof(x.soma.Vₘ, dvs) for x in neurons[5:end]])
109107
abovethold = reduce(hcat, interpolated.u) .> 10.0
110-
using Statistics
111108
final = mean(abovethold, dims = 1)'
112-
using Plots
113109
plot(final)
114110

demo/simplesynapse.jl

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Example of synapses
2-
using Conductor, OrdinaryDiffEq, Unitful, ModelingToolkit, Plots
2+
using Conductor, Unitful, ModelingToolkit, OrdinaryDiffEq, Plots
33
import Unitful: mV, mS, cm, µm, pA, nA, mA, µA, ms, nS, pS
44
import Conductor: Na, K
55

@@ -28,18 +28,15 @@ kdr_kinetics = [Gate(AlphaBeta,
2828
@named Kdr = IonChannel(Potassium, kdr_kinetics, max_g = 36mS / cm^2);
2929
@named leak = IonChannel(Leak, max_g = 0.3mS / cm^2);
3030

31-
channels = [NaV, Kdr, leak];
3231
reversals = Equilibria([Na => 50.0mV, K => -77.0mV, Leak => -54.4mV]);
33-
dynamics = HodgkinHuxley(channels, reversals);
32+
dynamics = HodgkinHuxley([NaV, Kdr, leak], reversals);
33+
geometry = Cylinder(radius = 25µm, height = 400µm)
3434

3535
# Stimulate first neuron with a holding current to drive APs
3636
@named holding_current = Bias(5000pA);
37-
@named neuron1 = Compartment(Vₘ, dynamics;
38-
geometry = Cylinder(radius = 25µm, height = 400µm),
39-
stimuli = [holding_current]);
37+
@named neurons = Population(Compartment(Vₘ, dynamics; geometry), 2);
4038

41-
@named neuron2 = Compartment(Vₘ, dynamics;
42-
geometry = Cylinder(radius = 25µm, height = 400µm));
39+
add_stimuli!(neurons, [holding_current], 1)
4340

4441
############################################################################################
4542
# Event-based Synaptic model
@@ -52,9 +49,9 @@ syn_kinetics = Gate(SimpleGate, m, [D(m) ~ -m/τsyn])
5249
event_model = ConstantValueEvent(m) # increment `m` gate by 1 for each incoming AP
5350
@named ExpAMPA = SynapticChannel(event_model, Cation, [syn_kinetics]; max_s = 30nS)
5451

55-
top = NetworkTopology([neuron1, neuron2], [ExpAMPA]);
52+
top = NetworkTopology(neurons, [ExpAMPA]);
5653

57-
top[neuron1, neuron2] = ExpAMPA(10nS)
54+
top[neurons[1], neurons[2]] = ExpAMPA(10nS)
5855
rev_map = Dict([ExpAMPA => EGlut])
5956

6057
@named net = NeuronalNetworkSystem(top, rev_map)
@@ -63,8 +60,8 @@ total_time = 250.0
6360
sim = Simulation(net, total_time * ms)
6461
sol = solve(sim, Rosenbrock23(), abstol=1e-3, reltol=1e-3);
6562

66-
plot(plot(sol, idxs = [neuron1.Vₘ]),
67-
plot(sol, idxs = [neuron2.Vₘ]),
63+
plot(plot(sol, idxs = [neurons[1].Vₘ]),
64+
plot(sol, idxs = [neurons[2].Vₘ]),
6865
layout=(2,1))
6966

7067
############################################################################################
@@ -78,18 +75,18 @@ syn_kinetics2 = Gate(SteadyStateTau, syn∞, tausyn, name = :z)
7875

7976
@named IntAMPA = SynapticChannel(IntegratedSynapse(), Cation, [syn_kinetics2]; max_s = 30nS)
8077

81-
top2 = NetworkTopology([neuron1, neuron2], [IntAMPA]);
78+
top2 = NetworkTopology(neurons, [IntAMPA]);
8279

83-
top2[neuron1, neuron2] = IntAMPA(30nS)
80+
top2[neurons[1], neurons[2]] = IntAMPA(40nS)
8481
rev_map2 = Dict([IntAMPA => EGlut])
8582

8683
@named net2 = NeuronalNetworkSystem(top2, rev_map2)
8784

8885
total_time = 250.0
89-
sim2 = Simulation(net2, total_time * ms)
86+
sim2 = Simulation(net2, (0.0ms, total_time * ms))
9087
sol2 = solve(sim2, Rosenbrock23(), abstol = 1e-3, reltol = 1e-3);
9188

92-
plot(plot(sol2, idxs = [neuron1.Vₘ]),
93-
plot(sol2, idxs = [neuron2.Vₘ]),
89+
plot(plot(sol2, idxs = [neurons[1].Vₘ]),
90+
plot(sol2, idxs = [neurons[2].Vₘ]),
9491
layout=(2,1))
9592

src/Conductor.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export output, get_output, timeconstant, steadystate, forward_rate, reverse_rate
107107
export Sphere, Cylinder, Point, Unitless, area, radius, height
108108

109109
export HodgkinHuxley
110-
export Bias, PulseTrain
110+
export Bias, PulseTrain, add_stimuli!
111111

112112
# Metadata IDs
113113
struct ConductorMaxConductance end
@@ -137,6 +137,7 @@ struct IntegratedSynapse <: SynapticModel end
137137

138138
export ConstantValueEvent, IntegratedSynapse
139139

140+
include("populations.jl")
140141
include("util.jl")
141142
include("primitives.jl")
142143
include("stimulus.jl")
@@ -148,6 +149,5 @@ include("compartment.jl")
148149
include("multicompartment.jl")
149150
include("network.jl")
150151
include("simulation.jl")
151-
include("populations.jl")
152152
include("callbacks.jl")
153153
end # module

src/multicompartment.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,17 @@ function Base.convert(::Type{ODESystem}, mcsys::MultiCompartmentSystem)
186186
systems = systems, checks = CheckComponents)
187187
return odesys
188188
end
189+
190+
function add_stimuli!(pop::Population{MultiCompartmentSystem},
191+
stimuli::Vector{<:StimulusModel}, idxs)
192+
nrn = pop.neurons[idxs[1]]
193+
subcomps = get_compartments(nrn)
194+
subcomps[idxs[2]] = remake(subcomps[idxs[2]]; stimuli = stimuli)
195+
top = get_topology(nrn)
196+
@set! top.compartments = subcomps
197+
nrn = remake(nrn, topology = top)
198+
pop.neurons[idxs[1]] = nrn
199+
return nothing
200+
end
201+
202+

src/network.jl

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,27 @@
22
struct NetworkTopology
33
multigraph::Dict{Any, SparseMatrixCSC{Float64, Int64}}
44
neurons::Vector
5+
lens::Vector{Int64}
6+
function NetworkTopology(multigraph::Dict, neurons::Vector, lens::Vector{Int64})
7+
new(multigraph, neurons, lens)
8+
end
59
end
610

711
synaptic_systems(topology::NetworkTopology) = keys(graph(topology))
812

9-
function NetworkTopology(neurons::Vector, synaptic_systems::Vector)
13+
function NetworkTopology(neurons, synaptic_systems::Vector)
1014
m = length(neurons)
11-
n = sum(length(neuron) for neuron in neurons)
15+
lens = [length(neuron) for neuron in neurons]
16+
n = sum(lens)
1217
multigraph = Dict(x => sparse(Int64[], Int64[], Float64[], n, m) for x in synaptic_systems)
13-
return NetworkTopology(multigraph, neurons)
18+
return NetworkTopology(multigraph, collect(neurons), lens)
1419
end
1520

1621
function NetworkTopology(g::SimpleDiGraph, neurons, synaptic_system,
1722
default_weight = get_gbar(synaptic_system))
1823
multigraph = Dict(synaptic_system => adjacency_matrix(g) * default_weight)
19-
return NetworkTopology(multigraph, neurons)
24+
lens = [length(neuron) for neuron in neurons]
25+
return NetworkTopology(multigraph, neurons, lens)
2026
end
2127

2228
neurons(topology::NetworkTopology) = getfield(topology, :neurons)
@@ -32,29 +38,25 @@ graph(topology::NetworkTopology) = getfield(topology, :multigraph)
3238
find_source(pre, topology) = findfirst(isequal(first(pre)), root_compartments(topology))
3339
find_target(post, topology) = findfirst(isequal(post), compartments(topology))
3440

35-
function add_synapse!(topology, pre, post, synaptic_system, weight)
41+
function add_synapse!(topology, pre::AbstractCompartmentSystem,
42+
post::AbstractCompartmentSystem, synaptic_system, weight)
3643
src = find_source(pre, topology)
3744
dst = find_target(post, topology)
3845
g = graph(topology)[synaptic_system]
3946
g[dst, src] = weight # underlying matrix is transposed
4047
end
4148

42-
# FIXME: need updates
43-
#=
44-
function remove_synapse!(topology, pre, post, synaptic_system)
45-
src = find_source(pre, topology)
46-
dst = find_target(post, topology)
49+
function add_synapse!(topology, pre::Integer, post::Integer, synaptic_system, weight)
4750
g = graph(topology)[synaptic_system]
48-
g[dst, src] = zero(Num)
49-
dropzeros!(g)
51+
g[post, pre] = weight # underlying matrix is transposed
5052
end
5153

52-
function add_layer!(topology, synaptic_system::ConductanceSystem,
53-
g = SimpleDiGraph(length(vertices(topology))),
54-
default_weight = get_gbar(synaptic_system))
55-
push!(graph(topology), synaptic_system => adjacency_matrix(g) * default_weight)
54+
function add_synapse!(topology, pre::Integer, post::Tuple{Integer,Integer},
55+
synaptic_system, weight)
56+
g = graph(topology)[synaptic_system]
57+
post_idx = sum(topology.lens[1:post[1]-1]) + post[2]
58+
g[post_idx, pre] = weight # underlying matrix is transposed
5659
end
57-
=#
5860

5961
Base.eltype(nt::NetworkTopology) = AbstractCompartmentSystem
6062
Base.length(nt::NetworkTopology) = length(neurons(nt))
@@ -146,7 +148,7 @@ synaptic_systems(sys::NeuronalNetworkSystem) = synaptic_systems(get_topology(sys
146148
compartments(sys::NeuronalNetworkSystem) = compartments(get_topology(sys))
147149

148150
function connect_synapses!(gen, syn_model, comps, topology, reversal_map)
149-
# this method assumes weights scale the size of the event/alpha)
151+
# this method assumes weights scale the size of the event/alpha)
150152
new_compartments = deepcopy(comps)
151153
reversal = reversal_map[syn_model]
152154
for (i,comp) in enumerate(new_compartments)
@@ -226,7 +228,6 @@ function NeuronalNetworkSystem(topology::NetworkTopology, reversal_map,
226228
for sys in synaptic_systems(topology)
227229
comps = connect_synapses!(gen, sys, comps, topology, reversal_map)
228230
end
229-
230231
for neuron in neurons(topology)
231232
if typeof(neuron) <: MultiCompartmentSystem
232233
mctop = get_topology(neuron)

0 commit comments

Comments
 (0)