You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* update Getting Started text
* change title and fix link in parkinsons
* add seed and update plot in resting_state
* remove outdated phrase
* Fix the Parkinson's circuit to match Liu et al. 2020
Looks like this replicates their dynamics now. Includes all the weights from their paper and splits the striatum into the three groups instead of wherever we got that one striatum block from.
* Prettify the introduction
* Fix Jansen-Rit docstring to show defaults
* Add dispatch of powerspectrum for neural masses
* Overhaul of Parkinson's JR tutorial
This has some pretty significant changes that should address nearly all the points
---------
Co-authored-by: agchesebro <[email protected]>
Copy file name to clipboardExpand all lines: docs/src/getting_started.jl
+3-6
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@
7
7
# - [Julia Tutorials & Workshops](https://julialang.org/learning/tutorials/), a collection of training materials from the official Julia website.
8
8
# - [Modern Julia Workflows](https://modernjuliaworkflows.org/), an introduction to how to write and share your Julia code effectively with tips & tricks.
9
9
10
-
# ## Getting Started with Neuroblox
10
+
# ## [Getting Started with Neuroblox](@id getting_started_julia)
11
11
12
12
# This example will introduce you to simulating brain dynamics using Neuroblox. We will create a simple oscillating circuit using two Wilson-Cowan neural mass models [1]. The Wilson-Cowan model is one of the most influential models in computational neuroscience [2], describing the dynamics of interactions between populations of excitatory and inhibitory neurons.
13
13
@@ -54,10 +54,7 @@ add_edge!(g, WC1 => WC2; weight = 7) ## connection from WC1 to WC2
54
54
add_edge!(g, WC2 => WC1; weight =4) ## connection from WC2 to WC1
55
55
add_edge!(g, WC2 => WC2; weight =-1) ## recurrent connection from WC2 to itself
56
56
57
-
# Here, we've created two Wilson-Cowan Blox and connected them as nodes in a directed graph. The `adj` matrix defines the weighted edges between these nodes. Each entry `adj[i,j]` represents how the output of blox `j` influences the input of blox `i`:
58
-
#
59
-
# - Diagonal elements (`adj[1,1]` and `adj[2,2]`): Self-connections, adding feedback to each blox.
60
-
# - Off-diagonal elements (`adj[1,2]` and `adj[2,1]`): Inter-blox connections, determining how each blox influences the other.
57
+
# Here, we've created two Wilson-Cowan blox and connected them as nodes in a directed graph. Each blox connects to itself and to the other blox.
61
58
62
59
# By default, the output of each Wilson-Cowan blox is its excitatory activity (E). The negative self-connections (-1) provide inhibitory feedback, while the positive inter-blox connections (6) provide strong excitatory coupling. This setup creates an oscillatory dynamic between the two Wilson-Cowan units.
63
60
@@ -78,7 +75,7 @@ sol = solve(prob, Rodas4(), saveat=0.1)
78
75
79
76
# ### Plotting simulation results
80
77
81
-
# Finally, let us plot the `E` and `I` states of the first component, `WC1`.
78
+
# Finally, let us plot the `E` and `I` states of the first component, `WC1`. To do this we will use the `state_timeseries` function that extracts the timeseries of a blox state from the solution object.
# # Building a model of Parkinson's disease using Neural Mass models
2
-
3
-
# In this example, we'll construct a model of Parkinson's disease using eight Jansen-Rit Neural Mass Models, based on the work of Liu et al. (2020) [1].
1
+
# # Building a model of the Basal Ganglia using Neural Mass models
2
+
3
+
# In this example, we will construct a model of Parkinson's disease using Jansen-Rit neural mass models, based on the work of Liu et al. (2020) [1].
4
+
# More generally, this tutorial shows how to build a simple version of the cortico-basal ganglia-thalamocortical neural loop, including both the [direct and indirect pathways](https://www.ncbi.nlm.nih.gov/books/NBK537141/).
5
+
# In this tutorial, we will specifically cover:
6
+
# - How to create a Jansen-Rit neural mass model in Neuroblox
7
+
# - How to create blocks with different default sets of parameters
8
+
# - Using symbolic hyperparameters during graph definition
9
+
# - Extracting specific solutions states for plotting time series data
10
+
# - Remaking an ``ODEProblem`` without needing to recompile an entire ``ODESystem``
11
+
# - Plotting basic spectrograms of simulations
4
12
5
13
# ## The Jansen-Rit Neural Mass Model
6
14
7
-
# The Jansen-Rit model [2] is another popular neural mass model that, like the Wilson-Cowan model from [Example 1](#example-1-creating-a-simple-neural-circuit), describes the average activity of neural populations. Each Jansen-Rit unit is defined by the following differential equations:
15
+
# The Jansen-Rit model [2] is another popular neural mass model that, like the [Wilson-Cowan model from the Getting Started](@ref getting_started_julia), describes the average activity of neural populations.
16
+
# [This resource](https://link.springer.com/referenceworkentry/10.1007/978-1-4614-7320-6_65-2) is a good general introduction to the Jansen-Rit model if you'd like to read more.
17
+
# Each Jansen-Rit unit is defined by the following differential equations:
# where $x$ represents the average postsynaptic membrane potential of the neural population, $y$ is an auxiliary variable, $\tau$ is the membrane time constant, $H$ is the maximum postsynaptic potential amplitude, $\lambda$ determines the maximum firing rate, and $\sum{jcn}$ represents the sum of all synaptic inputs to the population. The sigmoid function $S(x)$ models the population's firing rate response to input and is defined as:
17
-
18
-
19
27
# ```math
20
28
# S(x) = \frac{1}{1 + \text{exp}(-rx)}
21
29
# ```
22
30
23
31
# where $r$ controls the steepness of the sigmoid, affecting the population's sensitivity to input.
24
32
33
+
# This model consists of four main components:
34
+
# - A cortical column consisting of pyramidal cells ``PY``, an excitatory interneuron population ``EI``, and an inhibitory interneuron population ``II``
35
+
# - A striatal network consisting of two populations of medium spiny neurons, ``D1`` and ``D2`` (named for the different dopamine receptors), and fast-spiking interneurons ``FSI``
36
+
# - Subcortical structures including the subthalamic nucleus ``STH``, the external segment of the globus pallidus ``GPE``, and the internal segment of the globus pallidus ``GPI``
37
+
# - A thalamic oscillator ``Th``
38
+
39
+
# The connections between these oscillators are shown in the Neuroblox GUI below:
40
+
# NEED FIGURE HERE!!
41
+
42
+
25
43
# ## Setting Up the Model
26
44
27
-
# Let's start by importing the necessary libraries and defining our neural masses:
45
+
#We're now going to set up this model using code. Let's start by importing the necessary libraries and defining our neural masses:
28
46
29
47
using Neuroblox
30
48
using OrdinaryDiffEq
31
49
using CairoMakie
32
50
33
-
## Convert time units from seconds to milliseconds
34
-
τ_factor =1000
51
+
# The original paper uses parameters in seconds, but all models in Neuroblox have milliseconds as their time unit. We therefore specify a scaling factor to use the paramters from the paper:
52
+
53
+
τ_factor =1000; ## Convert time units from seconds to milliseconds
54
+
55
+
# Now we'll setup the neural masses. The values are all taken from the original paper [1], Table 1.
56
+
# Notice that some of these neural masses have the default Neuroblox parameters (e.g., ``PY`` uses default cortical parameters, so the user can simply specify ``cortical=true`` to access these).
57
+
# If you want to see the full list of defaults for the Jansen-Rit model, you can type ``?Jansen-Rit`` in your Julia REPL to view the docstring.
35
58
36
-
## Define Jansen-Rit neural masses for different brain regions
@named PY =JansenRit(cortical=true) ## default parameters cortical Jansen Rit blox
42
61
@named EI =JansenRit(τ=0.01*τ_factor, H=20/τ_factor, λ=5, r=5)
43
-
@named PY =JansenRit(cortical=true) # default parameters cortical Jansen Rit blox
44
62
@named II =JansenRit(τ=2.0*τ_factor, H=60/τ_factor, λ=5, r=5)
45
63
46
-
#Here, we've created eight Jansen-Rit neural masses representing different brain regions involved in Parkinson's disease. The `τ_factor` is used to convert time units from seconds (as in the original paper) to milliseconds (Neuroblox's default time unit).
# Now, let's create a graph representing our brain circuit. The nodes on this graph are the neural mass models defined aboe and the edges are the connections between the nodes based on the known anatomy of the basal ganglia-thalamocortical circuit.
51
-
#As an alternative to creating edges with an adjacency matrix, here we demonstrate a different approach by adding edges one by one. In this case, we set the connections specified in Table 2 of Liu et al. [1], although we only implement a subset of the nodes and edges to describe a simplified version of the model.
52
-
#Our connections share some common parameters which we define here, both as symbols and values, and use them as expressions for the weight of each connection :
53
-
78
+
#We define the connection weights as specified in Table 2 of [1], and the signs (excitatory vs inhibitory) based on the connections in Figure 1.
79
+
#Note that Julia can use any unicode character as a variable name, so we can use arrows in the names of the weights. If you're typing these from scratch, you can access these by typing ``\rightarrow``. For more examples, check out the [Julia documentation](https://docs.julialang.org/en/v1/manual/unicode-input/).
80
+
# We also use the ``@parameters`` macro to define the common connection parameters, which we can then use to define the connections. This is a macro from [ModelingToolkit](https://docs.sciml.ai/ModelingToolkit/stable/tutorials/ode_modeling/) in Julia, which you should look at if you want to build more general models.
54
81
g =MetaDiGraph() ## define an empty graph
55
82
56
-
params =@parameters C_Cor=60 C_BG_Th=60 C_Cor_BG_Th=5 C_BG_Th_Cor=5# define common connection parameters
83
+
params =@parameters C_Cor=3 C_BGTh=3 C_Cor➡BGTh=9.75 C_BGTh➡Cor=9.75## define common connection parameters using healthy parameter range
57
84
58
85
## Create connections
59
-
add_edge!(g, GPE => Str; weight =-0.5*C_BG_Th)
60
-
add_edge!(g, GPE => GPE; weight =-0.5*C_BG_Th)
61
-
add_edge!(g, GPE => STN; weight = C_BG_Th)
62
-
add_edge!(g, STN => GPE; weight =-0.5*C_BG_Th)
63
-
add_edge!(g, STN => PY; weight = C_Cor_BG_Th)
64
-
add_edge!(g, GPI => GPE; weight =-0.5*C_BG_Th)
65
-
add_edge!(g, GPI => STN; weight = C_BG_Th)
66
-
add_edge!(g, Th => GPI; weight =-0.5*C_BG_Th)
67
-
add_edge!(g, EI => Th; weight = C_BG_Th_Cor)
68
-
add_edge!(g, EI => PY; weight =6*C_Cor)
69
-
add_edge!(g, PY => EI; weight =4.8*C_Cor)
70
-
add_edge!(g, PY => II; weight =-1.5*C_Cor)
71
-
add_edge!(g, II => PY; weight =1.5*C_Cor)
72
-
add_edge!(g, II => II; weight =3.3*C_Cor)
73
-
add_edge!(g, Str => Str; weight =-0.5*C_BG_Th)
74
-
add_edge!(g, Str => GPE; weight = C_BG_Th)
75
-
add_edge!(g, GPE => Str; weight =-0.5*C_BG_Th)
76
-
add_edge!(g, GPE => Th; weight = C_Cor_BG_Th)
77
-
add_edge!(g, STN => Str; weight =-0.5*C_BG_Th)
78
-
add_edge!(g, STN => GPE; weight = C_BG_Th)
79
-
add_edge!(g, GPI => STN; weight =-0.5*C_BG_Th)
80
-
add_edge!(g, GPI => GPI; weight = C_BG_Th_Cor)
86
+
87
+
## thalamocortical connection
88
+
add_edge!(g, Th => EI; weight = C_BGTh➡Cor)
89
+
90
+
## remaining cortical → subcortical connections
91
+
add_edge!(g, PY => STH; weight = C_Cor➡BGTh)
92
+
add_edge!(g, PY => D1; weight = C_BGTh➡Cor)
93
+
add_edge!(g, PY => D2; weight = C_BGTh➡Cor)
94
+
add_edge!(g, PY => FSI; weight = C_BGTh➡Cor)
95
+
96
+
## basal ganglia ↔ thalamus connections
97
+
add_edge!(g, STH => GPE; weight = C_BGTh)
98
+
add_edge!(g, STH => GPI; weight = C_BGTh)
99
+
add_edge!(g, GPE => STH; weight =-0.5*C_BGTh)
100
+
add_edge!(g, GPE => GPE; weight =-0.5*C_BGTh)
101
+
add_edge!(g, GPE => GPI; weight =-0.5*C_BGTh)
102
+
add_edge!(g, GPE => FSI; weight =-0.5*C_BGTh)
103
+
add_edge!(g, FSI => D1; weight =-0.5*C_BGTh)
104
+
add_edge!(g, FSI => D2; weight =-0.5*C_BGTh)
105
+
add_edge!(g, FSI => FSI; weight =-0.5*C_BGTh)
106
+
add_edge!(g, D1 => D1; weight =-0.5*C_BGTh)
107
+
add_edge!(g, D1 => D2; weight =-0.5*C_BGTh)
108
+
add_edge!(g, D1 => GPI; weight =-0.5*C_BGTh)
109
+
add_edge!(g, D2 => D2; weight =-0.5*C_BGTh)
110
+
add_edge!(g, D2 => D1; weight =-0.5*C_BGTh)
111
+
add_edge!(g, D2 => GPE; weight =-0.5*C_BGTh)
112
+
add_edge!(g, GPI => Th; weight =-0.5*C_BGTh)
113
+
114
+
## corticocortical connections
115
+
add_edge!(g, PY => EI; weight =6*C_Cor)
116
+
add_edge!(g, PY => II; weight =1.5*C_Cor)
117
+
add_edge!(g, EI => PY; weight =4.8*C_Cor)
118
+
add_edge!(g, II => PY; weight =-1.5*C_Cor)
119
+
add_edge!(g, II => II; weight =-3.3*C_Cor);
81
120
82
121
# ## Creating the Model
83
122
84
123
# Let's build the complete model:
85
124
86
-
@named final_system =system_from_graph(g)
125
+
@named final_system =system_from_graph(g);
87
126
88
127
# This creates a differential equations system from our graph representation using ModelingToolkit and symbolically simplifies it for efficient computation.
89
128
90
129
# ## Simulating the Model
91
130
92
-
# Lastly, we create the `ODEProblem` for our system, select an algorithm, in this case `Tsit5()`, and simulate 1 second of brain activity.
131
+
# Lastly, we create the ``ODEProblem``` for our system, select an algorithm, in this case ``Tsit5()`` (see discussion in the previous tutorial about solver choices if you're interested), and simulate 1 second of brain activity.
132
+
133
+
sim_dur =1000.0## Simulate for 1 second
134
+
prob =ODEProblem(final_system, [], (0.0, sim_dur)) ## Create the problem to solve
135
+
sol =solve(prob, Tsit5(), saveat=0.1); ## Solve the problem and save every 0.1ms
136
+
137
+
# ## Visualizing the results
138
+
# Let's interrogate the solution to see what we have. For the purposes of this tutorial, we'll focus on the striatal oscillations.
139
+
# In this simple model, we should see relatively sharp on/off transitions in the striatal populations. To test this, let's use [Symbolic Indexing](https://docs.sciml.ai/SymbolicIndexingInterface/stable/usage/) to access the states we're interested in: the ``y`` state of the D1 neuron population.
140
+
141
+
idx_func = ModelingToolkit.getu(sol, D1.odesystem.y); ## gets the state index of the D1 neuron population in the solution object
142
+
143
+
# Now use this indexing function to plot the solution in a Makie plot ([read more about Makie in the docs](https://docs.makie.org/stable/tutorials/getting-started)).
144
+
145
+
fig =Figure() ## create the figure
146
+
ax =Axis(fig[1, 1], xlabel="Time (ms)", ylabel="y voltage (arbitrary units)", title="D1 population membrane voltage") ## define one axis for the figure
147
+
lines!(ax, sol.t, idx_func(sol), color =:blue) ## plot the time series of the D1 neuron population
148
+
display(fig) ## display the figure
149
+
150
+
# Great! The striatum is oscillating. A Parkinson's model is better evaluated in the frequency domain, so let's lengthen the simulation now and plot a spectrogram.
151
+
# To do this, we'll use the ``remake`` function to create a new ``ODEProblem`` with a longer simulation time without rebuilding the entire system.
152
+
153
+
sim_dur =10000.0## Simulate for 10 seconds
154
+
prob =remake(prob, tspan = (0.0, sim_dur)) ## Remake the ODEProblem with the new simulation time
155
+
sol =solve(prob, Tsit5(), saveat=0.1); ## Solve the new problem
156
+
157
+
# Let's use this new solution to plot the power spectrum of the D1 neuron population. This is using a built-in Neuroblox function ``powerspectrumplot`` that's discussed at length in the next tutorial, so we won't delve into the details of the arguments here.
158
+
159
+
powerspectrumplot(D1, sol, state ="y", ## specify the block to plot, the solution object, and the state of the block to plot
160
+
method = welch_pgram, window = hanning, ylims = (1e-6, 1e6), ## additional parameters to make the plot pretty
# You should see two prominent β-band peaks: one in low β (around 15 Hz) and one in high β (around 35 Hz). You should also see their resonances in the γ-band.
97
167
168
+
# ## Further Exploration
169
+
# You might be wondering: what about Parkinson's in this model? Well, like all good things, that is left as an exercise for the reader. Following the original paper, try replacing the original parameters with Parkinson's versions, so that
170
+
# ``C_Cor=60``, ``C_BGTh=60``, ``C_Cor➡BGTh=5``, and ``C_BGTh➡Cor=5``. If you just change these values, you should see the D1 population have a dramatic increase in β power at the lower frequency peak and a complete loss of the higher frequency peak.
171
+
# We've selected the plotting parameters so you can use the same plotting call above to see this difference - so no excuse not to run this on your own!
172
+
# If you're inspired to continue experimenting, here are a couple other ideas:
173
+
# - Try looking at the ``PY`` population under normal and Parkinson's conditions. Hint: you'll need to adjust the ``ylims`` of the power spectrum to fully appreciate the changes.
174
+
# - Try changing the lengths of the simulation and the sampling frequency. How does this affect the power spectrum?
98
175
99
176
# ## References
100
-
# [[1] Liu, C., Zhou, C., Wang, J., Fietkiewicz, C., & Loparo, K. A. (2020). The role of coupling connections in a model of the cortico-basal ganglia-thalamocortical neural loop for the generation of beta oscillations. Neural Networks, 123, 381-392.](https://doi.org/10.1016/j.neunet.2019.12.021)
177
+
# [1] Liu, C, Zhou, C, Wang, J, Fietkiewicz, C, & Loparo, KA. (2020). The role of coupling connections in a model of the cortico-basal ganglia-thalamocortical neural loop for the generation of beta oscillations. Neural Networks, 123, 381-392. DOI: [10.1016/j.neunet.2019.12.021](https://doi.org/10.1016/j.neunet.2019.12.021)
178
+
# [2] Jansen BH, Rit VG. Electroencephalogram and visual evoked potential generation in a mathematical model of coupled cortical columns. Biol Cybern. 1995 Sep;73(4):357-66. DOI: [10.1007/BF00199471](https://pubmed.ncbi.nlm.nih.gov/7578475/).
0 commit comments