Example 4: Vectorized Populations (E/I Network)

Let's scale up to population-level models. Instead of building thousands of individual scalar systems, we can use the Vectorized topology. A single GenericChannel with a vectorized topology expands to N elements. We wire dense excitatory/inhibitory populations together using a weight matrix and the build_synapse_block helper.

The main benefit of the Vectorized topology is that it saves compile time. Compiling 1,000 scalar systems requires MTK to process 1,000 separate symbolic graphs. Using Vectorized(1000) collapses the entire population into a single system that uses array operations.

Note

An annoyance is the need to build both scalar and vector definitions for the components. It would be nice (and maybe exists?) for MTK to have some automatic way of turning a scalar component into a vectorised one? I spent some time attempting this with postwalk, and got it running. But it was messy and dependeng on the MTK internal API so decided to demand a separate set of equations for a vectorised component.

using MTKNeuralToolkit
using ModelingToolkit: mtkcompile, @named
using OrdinaryDiffEq
using Plots

1. Define Shared Gating Dynamics

These functions use broadcasting (.) so they work identically for both scalar and vectorized topologies.

hh_na_m = v -> (
    0.182 .* (v .+ 35.0) ./ (1.0 .- exp.(-(v .+ 35.0) ./ 9.0)),
    -0.124 .* (v .+ 35.0) ./ (1.0 .- exp.((v .+ 35.0) ./ 9.0))
)
hh_na_h = v -> (
    0.25 .* exp.(-(v .+ 90.0) ./ 12.0),
    0.25 .* (exp.((v .+ 62.0) ./ 6.0)) ./ exp.(-(v .+ 90.0) ./ 12.0)
)
sodium_gates = [GateSpec(:m, 3, 0.0, hh_na_m), GateSpec(:h, 1, 0.0, hh_na_h)]

hh_k_n = v -> (
    0.02 .* (v .- 25.0) ./ (1.0 .- exp.(-(v .- 25.0) ./ 9.0)),
    -0.002 .* (v .- 25.0) ./ (1.0 .- exp.((v .- 25.0) ./ 9.0))
)
potassium_gates = [GateSpec(:n, 4, 0.0, hh_k_n)]
1-element Vector{GateSpec{Int64, Float64, Main.var"#8#9"}}:
 GateSpec{Int64, Float64, Main.var"#8#9"}(:n, 4, 0.0, Main.var"#8#9"())

2. Define Topologies and Build Populations

N_E = 15
N_I = 5
top_E = Vectorized(N_E)
top_I = Vectorized(N_I)

function build_population(name::Symbol, top)
    #Let's make the sodium conductance heterogeneous across the population
    gNa_heterogeneous = collect(range(119.0, 121.0, length=top.N))

    @named cap = Capacitor(topology=top, C=1.0)
    @named na  = GenericChannel(topology=top, g=gNa_heterogeneous, E_rev=50.0,  gates=sodium_gates)
    @named k   = GenericChannel(topology=top, g=36.0,  E_rev=-77.0, gates=potassium_gates)
    @named leak= GenericChannel(topology=top, g=0.3,   E_rev=-54.4, gates=GateSpec[])

    return build_compartment(cap, [na, k, leak]; name=name, V_init=-65.0, topology=top)
end

pop_E = build_population(:pop_E, top_E)
pop_I = build_population(:pop_I, top_I)
Compartment{NoMorphology, NoGeometry, Float64}(Model pop_I:
Subsystems (8): see hierarchy(pop_I)
  cap
  injector
  syn_injector
  p
  ⋮
Equations (76):
  60 standard: see equations(pop_I)
  16 connecting: see equations(expand_connections(pop_I))
Unknowns (51): see unknowns(pop_I)
  cap₊v(t)
  cap₊i(t)
  cap₊p₊v(t)
  cap₊p₊i(t)
  ⋮
Parameters (7): see parameters(pop_I)
  cap₊C
  na₊g
  na₊E_rev
  k₊g
  ⋮, (V = (pop_I₊cap₊v(t))[1:5], p_pin = Model pop_I₊p:
Equations (2):
  2 connecting: see equations(expand_connections(pop_I₊p))
Unknowns (2): see unknowns(pop_I₊p)
  v(t)
  i(t), n_pin = Model pop_I₊n:
Equations (2):
  2 connecting: see equations(expand_connections(pop_I₊n))
Unknowns (2): see unknowns(pop_I₊n)
  v(t)
  i(t), I_ext = (pop_I₊injector₊I₊u(t))[1:5], I_syn = (pop_I₊syn_injector₊I₊u(t))[1:5], cap_name = :cap), -65.0, Vectorized(5), NoGeometry(), NoMorphology())

3. Define Connectivity Matrices

The weight matrix W maps presynaptic populations to postsynaptic populations. Dimensions must be (Npost, Npre).

W_EE = 0.5 .* rand(N_E, N_E)   #E -> E
W_EI = 1.0 .* rand(N_I, N_E)   #E -> I
W_IE = 2.0 .* rand(N_E, N_I)   #I -> E
W_II = 1.0 .* rand(N_I, N_I)   #I -> I
5×5 Matrix{Float64}:
 0.293718  0.515388   0.620702   0.230925  0.699552
 0.735884  0.0192808  0.686515   0.162095  0.359942
 0.626611  0.551435   0.131316   0.575342  0.610688
 0.625633  0.642916   0.539218   0.791609  0.257407
 0.836875  0.743044   0.0776607  0.120448  0.577886

4. Build Synapse Blocks

build_synapse_block sets up the vectorized synapse matrices and creates the SynapseSpecs for the network builder.

syn_EE = build_synapse_block(pop_E, pop_E, W_EE; name=:syn_EE, E_rev=0.0)
syn_EI = build_synapse_block(pop_E, pop_I, W_EI; name=:syn_EI, E_rev=0.0)
syn_IE = build_synapse_block(pop_I, pop_E, W_IE; name=:syn_IE, E_rev=-80.0)
syn_II = build_synapse_block(pop_I, pop_I, W_II; name=:syn_II, E_rev=-80.0)

synapse_specs = [syn_EE, syn_EI, syn_IE, syn_II]
4-element Vector{SynapseSpec}:
 SynapseSpec((pop_E₊cap₊v(t))[1:15], (pop_E₊cap₊v(t))[1:15], (pop_E₊syn_injector₊I₊u(t))[1:15], Model syn_EE:
Equations (2):
  2 standard: see equations(syn_EE)
Unknowns (4): see unknowns(syn_EE)
  s(t)
  I_syn(t)
  V_pre(t)
  V_post(t)
Parameters (6): see parameters(syn_EE)
  g_max
  τ
  E_rev
  V_th
  ⋮, Compartment{NoMorphology, NoGeometry, Float64}(Model pop_E:
Subsystems (8): see hierarchy(pop_E)
  cap
  injector
  syn_injector
  p
  ⋮
Equations (136):
  120 standard: see equations(pop_E)
  16 connecting: see equations(expand_connections(pop_E))
Unknowns (51): see unknowns(pop_E)
  cap₊v(t)
  cap₊i(t)
  cap₊p₊v(t)
  cap₊p₊i(t)
  ⋮
Parameters (7): see parameters(pop_E)
  cap₊C
  na₊g
  na₊E_rev
  k₊g
  ⋮, (V = (pop_E₊cap₊v(t))[1:15], p_pin = Model pop_E₊p:
Equations (2):
  2 connecting: see equations(expand_connections(pop_E₊p))
Unknowns (2): see unknowns(pop_E₊p)
  v(t)
  i(t), n_pin = Model pop_E₊n:
Equations (2):
  2 connecting: see equations(expand_connections(pop_E₊n))
Unknowns (2): see unknowns(pop_E₊n)
  v(t)
  i(t), I_ext = (pop_E₊injector₊I₊u(t))[1:15], I_syn = (pop_E₊syn_injector₊I₊u(t))[1:15], cap_name = :cap), -65.0, Vectorized(15), NoGeometry(), NoMorphology()))
 SynapseSpec((pop_E₊cap₊v(t))[1:15], (pop_I₊cap₊v(t))[1:5], (pop_I₊syn_injector₊I₊u(t))[1:5], Model syn_EI:
Equations (2):
  2 standard: see equations(syn_EI)
Unknowns (4): see unknowns(syn_EI)
  s(t)
  I_syn(t)
  V_pre(t)
  V_post(t)
Parameters (6): see parameters(syn_EI)
  g_max
  τ
  E_rev
  V_th
  ⋮, Compartment{NoMorphology, NoGeometry, Float64}(Model pop_I:
Subsystems (8): see hierarchy(pop_I)
  cap
  injector
  syn_injector
  p
  ⋮
Equations (76):
  60 standard: see equations(pop_I)
  16 connecting: see equations(expand_connections(pop_I))
Unknowns (51): see unknowns(pop_I)
  cap₊v(t)
  cap₊i(t)
  cap₊p₊v(t)
  cap₊p₊i(t)
  ⋮
Parameters (7): see parameters(pop_I)
  cap₊C
  na₊g
  na₊E_rev
  k₊g
  ⋮, (V = (pop_I₊cap₊v(t))[1:5], p_pin = Model pop_I₊p:
Equations (2):
  2 connecting: see equations(expand_connections(pop_I₊p))
Unknowns (2): see unknowns(pop_I₊p)
  v(t)
  i(t), n_pin = Model pop_I₊n:
Equations (2):
  2 connecting: see equations(expand_connections(pop_I₊n))
Unknowns (2): see unknowns(pop_I₊n)
  v(t)
  i(t), I_ext = (pop_I₊injector₊I₊u(t))[1:5], I_syn = (pop_I₊syn_injector₊I₊u(t))[1:5], cap_name = :cap), -65.0, Vectorized(5), NoGeometry(), NoMorphology()))
 SynapseSpec((pop_I₊cap₊v(t))[1:5], (pop_E₊cap₊v(t))[1:15], (pop_E₊syn_injector₊I₊u(t))[1:15], Model syn_IE:
Equations (2):
  2 standard: see equations(syn_IE)
Unknowns (4): see unknowns(syn_IE)
  s(t)
  I_syn(t)
  V_pre(t)
  V_post(t)
Parameters (6): see parameters(syn_IE)
  g_max
  τ
  E_rev
  V_th
  ⋮, Compartment{NoMorphology, NoGeometry, Float64}(Model pop_E:
Subsystems (8): see hierarchy(pop_E)
  cap
  injector
  syn_injector
  p
  ⋮
Equations (136):
  120 standard: see equations(pop_E)
  16 connecting: see equations(expand_connections(pop_E))
Unknowns (51): see unknowns(pop_E)
  cap₊v(t)
  cap₊i(t)
  cap₊p₊v(t)
  cap₊p₊i(t)
  ⋮
Parameters (7): see parameters(pop_E)
  cap₊C
  na₊g
  na₊E_rev
  k₊g
  ⋮, (V = (pop_E₊cap₊v(t))[1:15], p_pin = Model pop_E₊p:
Equations (2):
  2 connecting: see equations(expand_connections(pop_E₊p))
Unknowns (2): see unknowns(pop_E₊p)
  v(t)
  i(t), n_pin = Model pop_E₊n:
Equations (2):
  2 connecting: see equations(expand_connections(pop_E₊n))
Unknowns (2): see unknowns(pop_E₊n)
  v(t)
  i(t), I_ext = (pop_E₊injector₊I₊u(t))[1:15], I_syn = (pop_E₊syn_injector₊I₊u(t))[1:15], cap_name = :cap), -65.0, Vectorized(15), NoGeometry(), NoMorphology()))
 SynapseSpec((pop_I₊cap₊v(t))[1:5], (pop_I₊cap₊v(t))[1:5], (pop_I₊syn_injector₊I₊u(t))[1:5], Model syn_II:
Equations (2):
  2 standard: see equations(syn_II)
Unknowns (4): see unknowns(syn_II)
  s(t)
  I_syn(t)
  V_pre(t)
  V_post(t)
Parameters (6): see parameters(syn_II)
  g_max
  τ
  E_rev
  V_th
  ⋮, Compartment{NoMorphology, NoGeometry, Float64}(Model pop_I:
Subsystems (8): see hierarchy(pop_I)
  cap
  injector
  syn_injector
  p
  ⋮
Equations (76):
  60 standard: see equations(pop_I)
  16 connecting: see equations(expand_connections(pop_I))
Unknowns (51): see unknowns(pop_I)
  cap₊v(t)
  cap₊i(t)
  cap₊p₊v(t)
  cap₊p₊i(t)
  ⋮
Parameters (7): see parameters(pop_I)
  cap₊C
  na₊g
  na₊E_rev
  k₊g
  ⋮, (V = (pop_I₊cap₊v(t))[1:5], p_pin = Model pop_I₊p:
Equations (2):
  2 connecting: see equations(expand_connections(pop_I₊p))
Unknowns (2): see unknowns(pop_I₊p)
  v(t)
  i(t), n_pin = Model pop_I₊n:
Equations (2):
  2 connecting: see equations(expand_connections(pop_I₊n))
Unknowns (2): see unknowns(pop_I₊n)
  v(t)
  i(t), I_ext = (pop_I₊injector₊I₊u(t))[1:5], I_syn = (pop_I₊syn_injector₊I₊u(t))[1:5], cap_name = :cap), -65.0, Vectorized(5), NoGeometry(), NoMorphology()))

5. Driving Stimuli & Network Assembly

Give the excitatory population a constant current kick to start the activity

drivers = [(1, 25.0)]

net = build_acausal_network([pop_E, pop_I]; synapse_specs=synapse_specs, drivers=drivers)

println("Compiling vectorized network...")
sys = mtkcompile(net.sys)
Model network:
Equations (120):
  120 standard: see equations(network)
Unknowns (120): see unknowns(network)
  (syn_II₊s(t))[5]
  (syn_II₊s(t))[4]
  (syn_II₊s(t))[3]
  (syn_II₊s(t))[2]
  ⋮
Parameters (38): see parameters(network)
  pop_E₊cap₊C
  pop_E₊na₊g
  pop_E₊na₊E_rev
  pop_E₊k₊g
  ⋮
Observed (1210): see observed(network)

Because the underlying equations are array-based, the Jacobian of this system is highly sparse: most neurons only affect their own gating variables, with off-diagonal elements coming only from the synapse blocks.

Passing jac=true, sparse=true tells the solver to compute an analytical Jacobian and leverage sparse linear algebra. This speeds simulation, as the solver only computes non-zero derivatives instead of a dense matrix. Sparse Jacobians are also essential for fast automatic differentiation (AD), which we will need later for parameter estimation.

prob = ODEProblem(sys, [], (0.0, 100.0), jac=true, sparse=true)
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true
Initialization status: FULLY_DETERMINED
Non-trivial mass matrix: false
timespan: (0.0, 100.0)
u0: 120-element Vector{Float64}:
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   ⋮
 -65.0
 -65.0
 -65.0
 -65.0
 -65.0
 -65.0
 -65.0
 -65.0
 -65.0

Let's visualize the sparsity pattern using Plots.spy. You'll see a heavy block-diagonal structure (intrinsic dynamics) with off-diagonal bands representing the synaptic couplings between the E and I populations.

println("Plotting Jacobian sparsity pattern...")
p_jac = spy(prob.f.jac_prototype,
            title="Jacobian Sparsity Pattern",
            legend=false)

println("Solving...")
sol = solve(prob, Rosenbrock23())
retcode: Success
Interpolation: specialized 2nd order "free" stiffness-aware interpolation
t: 170-element Vector{Float64}:
   0.0
   0.00022109928137169384
   0.006322968558070122
   0.012908472245188929
   0.027592911075486504
   0.04340941923912327
   0.06852261961711464
   0.09473710411660585
   0.12858911267378498
   0.1628765472243065
   ⋮
  46.796609907475926
  49.40383636050432
  52.37612250627013
  55.85307084547516
  60.05542870066455
  65.36109704350486
  72.49247892179994
  83.07758871178262
 100.0
u: 170-element Vector{Vector{Float64}}:
 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …  -65.0, -65.0, -65.0, -65.0, -65.0, -65.0, -65.0, -65.0, -65.0, -65.0]
 [3.741348999327893e-14, 3.741348999327893e-14, 3.741348999327893e-14, 3.741348999327893e-14, 3.741348999327893e-14, 3.741348999327893e-14, 3.741348999327893e-14, 3.741348999327893e-14, 3.741348999327893e-14, 3.741348999327893e-14  …  -64.99376962888171, -64.99376962888171, -64.99376962888171, -64.99376962888171, -64.99376962888171, -64.99376962888171, -64.99376962888171, -64.99376962888171, -64.99376962888171, -64.99376962888171]
 [1.0744902781608568e-12, 1.0744902781608548e-12, 1.074490278160883e-12, 1.0744902781609523e-12, 1.0744902781608814e-12, 1.0744902781608568e-12, 1.0744902781608548e-12, 1.074490278160883e-12, 1.0744902781609523e-12, 1.0744902781608814e-12  …  -64.82198761113345, -64.82198761113384, -64.8219876111336, -64.82198761113368, -64.82198761113372, -64.82198761113378, -64.82198761113386, -64.82198761113368, -64.82198761113375, -64.8219876111333]
 [2.2036710858445703e-12, 2.203671085844381e-12, 2.2036710858444257e-12, 2.203671085844793e-12, 2.203671085844109e-12, 2.2036710858445703e-12, 2.203671085844381e-12, 2.2036710858444257e-12, 2.203671085844793e-12, 2.203671085844109e-12  …  -64.6369426305452, -64.63694263054705, -64.63694263054623, -64.63694263054684, -64.63694263054725, -64.6369426305477, -64.63694263054828, -64.6369426305477, -64.6369426305482, -64.63694263054651]
 [4.7589367172986e-12, 4.758936717282059e-12, 4.758936717267898e-12, 4.758936717257087e-12, 4.758936717235552e-12, 4.7589367172986e-12, 4.758936717282059e-12, 4.758936717267898e-12, 4.758936717257087e-12, 4.758936717235552e-12  …  -64.22564082712458, -64.22564082714207, -64.22564082714662, -64.22564082715824, -64.22564082716877, -64.22564082717938, -64.22564082719084, -64.22564082719657, -64.22564082720751, -64.22564082720797]
 [7.569957671979513e-12, 7.569957671741674e-12, 7.569957671513511e-12, 7.569957671299097e-12, 7.569957671040703e-12, 7.569957671979513e-12, 7.569957671741674e-12, 7.569957671513511e-12, 7.569957671299097e-12, 7.569957671040703e-12  …  -63.78465250876654, -63.78465250888663, -63.784652508972485, -63.78465250907716, -63.78465250917874, -63.78465250928032, -63.78465250938466, -63.78465250947366, -63.784652509576155, -63.7846525096514]
 [1.2161204179496547e-11, 1.2161204176036161e-11, 1.2161204172617771e-11, 1.2161204169260203e-11, 1.2161204165708453e-11, 1.2161204179496547e-11, 1.2161204176036161e-11, 1.2161204172617771e-11, 1.2161204169260203e-11, 1.2161204165708453e-11  …  -63.08874139931318, -63.08874140027298, -63.088741401137135, -63.0887414020543, -63.08874140296196, -63.08874140386861, -63.088741404785274, -63.08874140565839, -63.08874140656775, -63.088741407403305]
 [1.7126294724993033e-11, 1.71262947029551e-11, 1.7126294681040303e-11, 1.7126294659308042e-11, 1.7126294636994726e-11, 1.7126294724993033e-11, 1.71262947029551e-11, 1.7126294681040303e-11, 1.7126294659308042e-11, 1.7126294636994726e-11  …  -62.3678816018723, -62.36788160629804, -62.367881610516584, -62.36788161485086, -62.36788161916265, -62.36788162347001, -62.367881627804216, -62.36788163204256, -62.36788163635613, -62.367881640515066]
 [2.3807608330620574e-11, 2.3807608205740532e-11, 2.3807608081218582e-11, 2.3807607957242616e-11, 2.380760783153432e-11, 2.3807608330620574e-11, 2.3807608205740532e-11, 2.3807608081218582e-11, 2.3807607957242616e-11, 2.380760783153432e-11  …  -61.44533984025383, -61.44533985834181, -61.44533987597528, -61.44533989386511, -61.44533991170027, -61.44533992951922, -61.44533994741184, -61.44533996508994, -61.445339982923485, -61.445340000432964]
 [3.0897266033488875e-11, 3.089726556880902e-11, 3.089726510498638e-11, 3.089726464250666e-11, 3.089726417577863e-11, 3.0897266033488875e-11, 3.089726556880902e-11, 3.089726510498638e-11, 3.089726464250666e-11, 3.089726417577863e-11  …  -60.520415965418714, -60.52041601809715, -60.52041606989516, -60.520416122194476, -60.52041617437776, -60.52041622651736, -60.52041627882764, -60.5204163307144, -60.52041638288409, -60.520416434454745]
 ⋮
 [0.0001624163186627216, 0.00014447026108849943, 0.031769164599961486, 4.9904803514510405, 0.00020405550956191187, 0.0001624163186627216, 0.00014447026108849943, 0.031769164599961486, 4.9904803514510405, 0.00020405550956191187  …  -60.991067017965214, -63.566976334742954, -41.62082547800239, -50.30307508220297, -56.11606433702249, -57.63242381713405, -56.37764917349946, -48.06311069464399, -39.61566864894115, -4.215034990483083]
 [9.582947536925707e-5, 8.761645573728094e-5, 0.03179699064571945, 4.99164372669057, 0.00012044919724276507, 9.582947536925707e-5, 8.761645573728094e-5, 0.03179699064571945, 4.99164372669057, 0.00012044919724276507  …  -60.99399568243333, -63.56712837999493, -41.620486939807634, -50.305051844554185, -56.117144590164685, -57.63239052941331, -56.378380923808365, -48.063670196472906, -39.614884536335275, -4.236869506105834]
 [5.2398601293909055e-5, 5.053314583786381e-5, 0.031823342909218844, 4.992347920757826, 6.591738340473133e-5, 5.2398601293909055e-5, 5.053314583786381e-5, 0.031823342909218844, 4.992347920757826, 6.591738340473133e-5  …  -60.995816243500016, -63.567104886688135, -41.62013323677682, -50.3061851464182, -56.11771962257995, -57.632245692957476, -56.378726386584134, -48.06388554245371, -39.614299791785534, -4.250567538421499]
 [2.5749129450929492e-5, 2.777826470006057e-5, 0.0318466289528062, 4.992728280999096, 3.245623999041034e-5, 2.5749129450929492e-5, 2.777826470006057e-5, 0.0318466289528062, 4.992728280999096, 3.245623999041034e-5  …  -60.996839809792654, -63.5669694519263, -41.61977760193019, -50.306723611999516, -56.117941230091624, -57.632031788099496, -56.37880526953983, -48.06386582330264, -39.613853553935606, -4.258585857578371]
 [1.0808914270272886e-5, 1.5021466336794015e-5, 0.03186588852243806, 4.992893941443233, 1.3697231629879646e-5, 1.0808914270272886e-5, 1.5021466336794015e-5, 0.03186588852243806, 4.992893941443233, 1.3697231629879646e-5  …  -60.997322210703786, -63.5667787901686, -41.61944263364391, -50.306875204079965, -56.117939181143846, -57.63179131668903, -56.37872166339439, -48.0637084173989, -39.61351215609318, -4.262827300398757]
 [3.526238391010662e-6, 8.80339355724135e-6, 0.03188060692272243, 4.992932968234592, 4.553049868344502e-6, 3.526238391010662e-6, 8.80339355724135e-6, 0.03188060692272243, 4.992932968234592, 4.553049868344502e-6  …  -60.99747427145324, -63.566582923902146, -41.6191558284053, -50.306813832798625, -56.117824126147376, -57.63156502206536, -56.37856557330866, -48.06349935605549, -39.61326029241735, -4.26474403313707]
 [7.184244252453129e-7, 6.406455169084106e-6, 0.03189062154240156, 4.99291428744531, 1.0275334688220505e-6, 7.184244252453129e-7, 6.406455169084106e-6, 0.03189062154240156, 4.99291428744531, 1.0275334688220505e-6  …  -60.997464643541036, -63.566422843602936, -41.618943371663605, -50.30667923276882, -56.117686000706186, -57.631387935548, -56.37841060116474, -48.06330985206549, -39.61309241006781, -4.265403900394342]
 [3.431557205988831e-8, 5.822862036993118e-6, 0.031896150512016656, 4.992886408299242, 1.6856295233553676e-7, 3.431557205988831e-8, 5.822862036993118e-6, 0.031896150512016656, 4.992886408299242, 1.6856295233553676e-7  …  -60.997415047122466, -63.5663247047783, -41.61882069484385, -50.30656929210111, -56.11758703878485, -57.63128209338925, -56.3783068022658, -48.063187761837376, -39.613002868622, -4.265528870011105]
 [-2.762162237113372e-9, 5.791467653011223e-6, 0.03189795090538837, 4.9928740108859, 1.2201128187813394e-7, -2.762162237113372e-9, 5.791467653011223e-6, 0.03189795090538837, 4.9928740108859, 1.2201128187813394e-7  …  -60.99739052887798, -63.5662916085663, -41.61878107618241, -50.30652738225308, -56.117551211864544, -57.63124705512299, -56.37827035177949, -48.06314570328087, -39.61297594856141, -4.265522958015287]

6. Plot the Results

We splat the voltage array (...) to plot all individual elements in the population. Couldn't find a way to add numberings with MTK but I assume it exists...?

p1 = plot(sol, idxs=[sys.pop_E.cap.v...],
          title="Excitatory Population", legend=false, ylabel="V (mV)")
p2 = plot(sol, idxs=[sys.pop_I.cap.v...],
          title="Inhibitory Population", legend=false, ylabel="V (mV)", xlabel="Time (ms)")
Example block output

Combine the simulation plots with the sparsity plot

plot(p1, p2, p_jac, layout=(3,1), size=(800, 800))
Example block output

This page was generated using Literate.jl.