diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 3334281..1059eae 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.2","generation_timestamp":"2024-12-30T20:53:30","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.2","generation_timestamp":"2025-01-02T13:38:49","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index 3d5b972..4671f55 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,6 +1,6 @@ -API · Neuroblox

API Documentation

Neuroblox.BalloonModelType

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • lnκ: logarithmic prefactor to signal decay H[1], set to 0 for standard parameter value.
  • lnτ: logarithmic prefactor to transit time H[3], set to 0 for standard parameter value.
  • lnϵ: logarithm of ratio of intra- to extra-vascular signal

NB: the prefix ln of the variables u, ν, q as well as the parameters κ, τ denotes their transformation into logarithmic space to enforce their positivity. This transformation is considered in the derivates of the model equations below.

Citations:

  1. Stephan K E, Weiskopf N, Drysdale P M, Robinson P A, and Friston K J. Comparing Hemodynamic Models with DCM. NeuroImage 38, no. 3 (2007): 387–401. doi: 10.1016/j.neuroimage.2007.07.040
  2. Hofmann D, Chesebro A G, Rackauckas C, Mujica-Parodi L R, Friston K J, Edelman A, and Strey H H. Leveraging Julia's Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed, 2023. doi: 10.1101/2023.10.27.564407
source
Neuroblox.DBSMethod
DBS(; name, namespace=nothing, frequency=130.0, amplitude=2.5, pulse_width=0.066, 
-    offset=0.0, start_time=0.0, smooth=1e-4)

Create a continuous deep brain stimulation (DBS) stimulus with regular pulses.

Arguments:

  • name: Name given to ODESystem object within the blox
  • namespace: Additional namespace above name if needed for inheritance
  • frequency: Pulse frequency in Hz
  • amplitude: Pulse amplitude in arbitrary units
  • pulse_width: Duration of each pulse in ms
  • offset: Baseline value of the signal between pulses
  • start_time: Time delay before stimulation begins in ms
  • smooth: Smoothing parameter for pulse transitions, set to 0 for sharp transitions

Returns a DBS stimulus blox that outputs square pulses with specified parameters.

source
Neuroblox.Generic2dOscillatorType
Generic2dOscillator(name, namespace, ...)
+API · Neuroblox

API Documentation

Neuroblox.BalloonModelType

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • lnκ: logarithmic prefactor to signal decay H[1], set to 0 for standard parameter value.
  • lnτ: logarithmic prefactor to transit time H[3], set to 0 for standard parameter value.
  • lnϵ: logarithm of ratio of intra- to extra-vascular signal

NB: the prefix ln of the variables u, ν, q as well as the parameters κ, τ denotes their transformation into logarithmic space to enforce their positivity. This transformation is considered in the derivates of the model equations below.

Citations:

  1. Stephan K E, Weiskopf N, Drysdale P M, Robinson P A, and Friston K J. Comparing Hemodynamic Models with DCM. NeuroImage 38, no. 3 (2007): 387–401. doi: 10.1016/j.neuroimage.2007.07.040
  2. Hofmann D, Chesebro A G, Rackauckas C, Mujica-Parodi L R, Friston K J, Edelman A, and Strey H H. Leveraging Julia's Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed, 2023. doi: 10.1101/2023.10.27.564407
source
Neuroblox.DBSMethod
DBS(; name, namespace=nothing, frequency=130.0, amplitude=2.5, pulse_width=0.066, 
+    offset=0.0, start_time=0.0, smooth=1e-4)

Create a continuous deep brain stimulation (DBS) stimulus with regular pulses.

Arguments:

  • name: Name given to ODESystem object within the blox
  • namespace: Additional namespace above name if needed for inheritance
  • frequency: Pulse frequency in Hz
  • amplitude: Pulse amplitude in arbitrary units
  • pulse_width: Duration of each pulse in ms
  • offset: Baseline value of the signal between pulses
  • start_time: Time delay before stimulation begins in ms
  • smooth: Smoothing parameter for pulse transitions, set to 0 for sharp transitions

Returns a DBS stimulus blox that outputs square pulses with specified parameters.

source
Neuroblox.Generic2dOscillatorType
Generic2dOscillator(name, namespace, ...)
 
 The Generic2dOscillator model is a generic dynamic system with two state
 variables. The dynamic equations of this model are composed of two ordinary
@@ -17,15 +17,15 @@
         \dot{V} &= d \, \tau (-f V^3 + e V^2 + g V + \alpha W + \gamma I) \\
         \dot{W} &= \dfrac{d}{	au}\,\,(c V^2 + b V - \beta W + a)
         \end{align}
-```

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • Other parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds.

Citations: FitzHugh, R., Impulses and physiological states in theoretical models of nerve membrane, Biophysical Journal 1: 445, 1961.

Nagumo et.al, An Active Pulse Transmission Line Simulating Nerve Axon, Proceedings of the IRE 50: 2061, 1962.

Stefanescu, R., Jirsa, V.K. Reduced representations of heterogeneous mixed neural networks with synaptic coupling. Physical Review E, 83, 2011.

Jirsa VK, Stefanescu R. Neural population modes capture biologically realistic large-scale network dynamics. Bulletin of Mathematical Biology, 2010.

Stefanescu, R., Jirsa, V.K. A low dimensional description of globally coupled heterogeneous neural networks of excitatory and inhibitory neurons. PLoS Computational Biology, 4(11), 2008).

source
Neuroblox.HarmonicOscillatorType
HarmonicOscillator(name, namespace, ω, ζ, k, h)
+```

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • Other parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds.

Citations: FitzHugh, R., Impulses and physiological states in theoretical models of nerve membrane, Biophysical Journal 1: 445, 1961.

Nagumo et.al, An Active Pulse Transmission Line Simulating Nerve Axon, Proceedings of the IRE 50: 2061, 1962.

Stefanescu, R., Jirsa, V.K. Reduced representations of heterogeneous mixed neural networks with synaptic coupling. Physical Review E, 83, 2011.

Jirsa VK, Stefanescu R. Neural population modes capture biologically realistic large-scale network dynamics. Bulletin of Mathematical Biology, 2010.

Stefanescu, R., Jirsa, V.K. A low dimensional description of globally coupled heterogeneous neural networks of excitatory and inhibitory neurons. PLoS Computational Biology, 4(11), 2008).

source
Neuroblox.HarmonicOscillatorType
HarmonicOscillator(name, namespace, ω, ζ, k, h)
 
 Create a harmonic oscillator blox with the specified parameters.
 The formal definition of this blox is:

\[\frac{dx}{dt} = y-(2*\omega*\zeta*x)+ k*(2/\pi)*(atan((\sum{jcn})/h) -\frac{dy}{dt} = -(\omega^2)*x\]

where ``jcn`` is any input to the blox.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • ω: Base frequency. Note the default value is scaled to give oscillations in milliseconds to match other blocks.
  • ζ: Damping ratio.
  • k: Gain.
  • h: Threshold.
source
Neuroblox.JansenRitType
JansenRit(name, namespace, τ, H, λ, r, cortical, delayed)
+\frac{dy}{dt} = -(\omega^2)*x\]

where ``jcn`` is any input to the blox.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • ω: Base frequency. Note the default value is scaled to give oscillations in milliseconds to match other blocks.
  • ζ: Damping ratio.
  • k: Gain.
  • h: Threshold.
source
Neuroblox.JansenRitType
JansenRit(name, namespace, τ, H, λ, r, cortical, delayed)
 
 Create a Jansen Rit blox as described in Liu et al.
 The formal definition of this blox is:

\[\frac{dx}{dt} = y-\frac{2}{\tau}x -\frac{dy}{dt} = -\frac{x}{\tau^2} + \frac{H}{\tau} [\frac{2\lambda}{1+\text{exp}(-r*\sum{jcn})} - \lambda]\]

where $jcn$ is any input to the blox.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • τ: Time constant. Defaults to 1 for cortical regions, 14 for subcortical.
  • H: See equation for use. Defaults to 0.02 for both cortical and subcortical regions.
  • λ: See equation for use. Defaults to 5 for cortical regions, 400 for subcortical.
  • r: See equation for use. Defaults to 0.15 for cortical regions, 0.1 for subcortical.
  • cortical: Boolean to determine whether to use cortical or subcortical parameters. Specifying any of the parameters above will override this.
  • delayed: Boolean to indicate whether states are delayed

Citations:

  1. Liu C, Zhou C, Wang J, Fietkiewicz C, Loparo KA. The role of coupling connections in a model of the cortico-basal ganglia-thalamocortical neural loop for the generation of beta oscillations. Neural Netw. 2020 Mar;123:381-392. doi: 10.1016/j.neunet.2019.12.021.
source
Neuroblox.KuramotoOscillatorType
KuramotoOscillator(name, namespace, ...)
+\frac{dy}{dt} = -\frac{x}{\tau^2} + \frac{H}{\tau} [\frac{2\lambda}{1+\text{exp}(-r*\sum{jcn})} - \lambda]\]

where $jcn$ is any input to the blox.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • τ: Time constant. Defaults to 1 for cortical regions, 14 for subcortical.
  • H: See equation for use. Defaults to 0.02 for both cortical and subcortical regions.
  • λ: See equation for use. Defaults to 5 for cortical regions, 400 for subcortical.
  • r: See equation for use. Defaults to 0.15 for cortical regions, 0.1 for subcortical.
  • cortical: Boolean to determine whether to use cortical or subcortical parameters. Specifying any of the parameters above will override this.
  • delayed: Boolean to indicate whether states are delayed

Citations:

  1. Liu C, Zhou C, Wang J, Fietkiewicz C, Loparo KA. The role of coupling connections in a model of the cortico-basal ganglia-thalamocortical neural loop for the generation of beta oscillations. Neural Netw. 2020 Mar;123:381-392. doi: 10.1016/j.neunet.2019.12.021.
source
Neuroblox.KuramotoOscillatorType
KuramotoOscillator(name, namespace, ...)
 
 Simple implementation of the Kuramoto oscillator as described in the original paper [1].
 Useful for general models of synchronization and oscillatory behavior.
@@ -47,29 +47,29 @@
         \end{equation}
 ```
 
-where $W_i$ is a Wiener process and $\zeta_i$ is the noise strength.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • Other parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds. Default parameter values are taken from [2].

Citations:

  1. Kuramoto, Y. (1975). Self-entrainment of a population of coupled non-linear oscillators. In: Araki, H. (eds) International Symposium on Mathematical Problems in Theoretical Physics. Lecture Notes in Physics, vol 39. Springer, Berlin, Heidelberg. https://doi.org/10.1007/BFb0013365

  2. Sermon JJ, Wiest C, Tan H, Denison T, Duchet B. Evoked resonant neural activity long-term dynamics can be reproduced by a computational model with vesicle depletion. Neurobiol Dis. 2024 Jun 14;199:106565. doi: 10.1016/j.nbd.2024.106565. Epub ahead of print. PMID: 38880431.

source
Neuroblox.LarterBreakspearType
LarterBreakspear(name, namespace, ...)
+where $W_i$ is a Wiener process and $\zeta_i$ is the noise strength.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • Other parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds. Default parameter values are taken from [2].

Citations:

  1. Kuramoto, Y. (1975). Self-entrainment of a population of coupled non-linear oscillators. In: Araki, H. (eds) International Symposium on Mathematical Problems in Theoretical Physics. Lecture Notes in Physics, vol 39. Springer, Berlin, Heidelberg. https://doi.org/10.1007/BFb0013365

  2. Sermon JJ, Wiest C, Tan H, Denison T, Duchet B. Evoked resonant neural activity long-term dynamics can be reproduced by a computational model with vesicle depletion. Neurobiol Dis. 2024 Jun 14;199:106565. doi: 10.1016/j.nbd.2024.106565. Epub ahead of print. PMID: 38880431.

source
Neuroblox.LarterBreakspearType
LarterBreakspear(name, namespace, ...)
 
 Create a Larter Breakspear blox described in Endo et al. For a full list of the parameters used see the reference.
-If you need to modify the parameters, see Chesebro et al. and van Nieuwenhuizen et al. for physiological ranges.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • Other parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds.

Citations:

  1. Endo H, Hiroe N, Yamashita O. Evaluation of Resting Spatio-Temporal Dynamics of a Neural Mass Model Using Resting fMRI Connectivity and EEG Microstates. Front Comput Neurosci. 2020 Jan 17;13:91. doi: 10.3389/fncom.2019.00091.
  2. Chesebro AG, Mujica-Parodi LR, Weistuch C. Ion gradient-driven bifurcations of a multi-scale neuronal model. Chaos Solitons Fractals. 2023 Feb;167:113120. doi: 10.1016/j.chaos.2023.113120.
  3. van Nieuwenhuizen, H, Chesebro, AG, Polis, C, Clarke, K, Strey, HH, Weistuch, C, Mujica-Parodi, LR. Ketosis regulates K+ ion channels, strengthening brain-wide signaling disrupted by age. Preprint. bioRxiv 2023.05.10.540257; doi: https://doi.org/10.1101/2023.05.10.540257.
source
Neuroblox.OUBloxType

Ornstein-Uhlenbeck process Blox

variables: x(t): value jcn: input parameters: τ: relaxation time μ: average value σ: random noise (variance of OU process is τ*σ^2/2) returns: an ODE System (but with brownian parameters)

source
Neuroblox.PINGNeuronExciType
PINGNeuronExci(name, namespace, C, g_Na, V_Na, g_K, V_K, g_L, V_L, I_ext, τ_R, τ_D)
+If you need to modify the parameters, see Chesebro et al. and van Nieuwenhuizen et al. for physiological ranges.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • Other parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds.

Citations:

  1. Endo H, Hiroe N, Yamashita O. Evaluation of Resting Spatio-Temporal Dynamics of a Neural Mass Model Using Resting fMRI Connectivity and EEG Microstates. Front Comput Neurosci. 2020 Jan 17;13:91. doi: 10.3389/fncom.2019.00091.
  2. Chesebro AG, Mujica-Parodi LR, Weistuch C. Ion gradient-driven bifurcations of a multi-scale neuronal model. Chaos Solitons Fractals. 2023 Feb;167:113120. doi: 10.1016/j.chaos.2023.113120.
  3. van Nieuwenhuizen, H, Chesebro, AG, Polis, C, Clarke, K, Strey, HH, Weistuch, C, Mujica-Parodi, LR. Ketosis regulates K+ ion channels, strengthening brain-wide signaling disrupted by age. Preprint. bioRxiv 2023.05.10.540257; doi: https://doi.org/10.1101/2023.05.10.540257.
source
Neuroblox.OUBloxType

Ornstein-Uhlenbeck process Blox

variables: x(t): value jcn: input parameters: τ: relaxation time μ: average value σ: random noise (variance of OU process is τ*σ^2/2) returns: an ODE System (but with brownian parameters)

source
Neuroblox.PINGNeuronExciType
PINGNeuronExci(name, namespace, C, g_Na, V_Na, g_K, V_K, g_L, V_L, I_ext, τ_R, τ_D)
 
 Create an excitatory neuron from Borgers et al. (2008).
 The formal definition of this blox is:

\[\frac{dV}{dt} = \frac{1}{C}(-g_{Na}*m_{\infty}^3*h*(V - V_{Na}) - g_K*n^4*(V - V_K) - g_L*(V - V_L) + I_{ext} + jcn) \m_{\infty} = \frac{a_m(V)}{a_m(V) + b_m(V)} \frac{dn}{dt} = a_n(V)*(1 - n) - b_n(V)*n \frac{dh}{dt} = a_h(V)*(1 - h) - b_h(V)*h -\frac{ds}{dt} = \frac{1}{2}*(1 + \tanh(V/10))*(\frac{1 - s}{\tau_R} - \frac{s}{\tau_D})\]

where $jcn$ is any input to the blox. Note that this is a modified Hodgkin-Huxley formalism with an additional synaptic accumulation term. Synapses are added into the $jcn$ term by connecting the postsynaptic neuron's voltage to the presynaptic neuron's output:

\[jcn = w*s*(V_E - V)\]

where $w$ is the weight of the synapse and $V_E$ is the reversal potential of the excitatory synapse.

Inputs:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • C: Membrane capacitance (defaults to 1.0).
  • g_Na: Sodium conductance (defaults to 100.0).
  • V_Na: Sodium reversal potential (defaults to 50.0).
  • g_K: Potassium conductance (defaults to 80.0).
  • V_K: Potassium reversal potential (defaults to -100.0).
  • g_L: Leak conductance (defaults to 0.1).
  • V_L: Leak reversal potential (defaults to -67.0).
  • I_ext: External current (defaults to 0.0).
  • τ_R: Rise time of synaptic conductance (defaults to 0.2).
  • τ_D: Decay time of synaptic conductance (defaults to 2.0).
source
Neuroblox.PINGNeuronInhibType
PINGNeuronInhib(name, namespace, C, g_Na, V_Na, g_K, V_K, g_L, V_L, I_ext, τ_R, τ_D)
+\frac{ds}{dt} = \frac{1}{2}*(1 + \tanh(V/10))*(\frac{1 - s}{\tau_R} - \frac{s}{\tau_D})\]

where $jcn$ is any input to the blox. Note that this is a modified Hodgkin-Huxley formalism with an additional synaptic accumulation term. Synapses are added into the $jcn$ term by connecting the postsynaptic neuron's voltage to the presynaptic neuron's output:

\[jcn = w*s*(V_E - V)\]

where $w$ is the weight of the synapse and $V_E$ is the reversal potential of the excitatory synapse.

Inputs:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • C: Membrane capacitance (defaults to 1.0).
  • g_Na: Sodium conductance (defaults to 100.0).
  • V_Na: Sodium reversal potential (defaults to 50.0).
  • g_K: Potassium conductance (defaults to 80.0).
  • V_K: Potassium reversal potential (defaults to -100.0).
  • g_L: Leak conductance (defaults to 0.1).
  • V_L: Leak reversal potential (defaults to -67.0).
  • I_ext: External current (defaults to 0.0).
  • τ_R: Rise time of synaptic conductance (defaults to 0.2).
  • τ_D: Decay time of synaptic conductance (defaults to 2.0).
source
Neuroblox.PINGNeuronInhibType
PINGNeuronInhib(name, namespace, C, g_Na, V_Na, g_K, V_K, g_L, V_L, I_ext, τ_R, τ_D)
 
 Create an inhibitory neuron from Borgers et al. (2008).
 The formal definition of this blox is:

\[\frac{dV}{dt} = \frac{1}{C}(-g_{Na}*m_{\infty}^3*h*(V - V_{Na}) - g_K*n^4*(V - V_K) - g_L*(V - V_L) + I_{ext} + jcn) \m_{\infty} = \frac{a_m(V)}{a_m(V) + b_m(V)} \frac{dn}{dt} = a_n(V)*(1 - n) - b_n(V)*n \frac{dh}{dt} = a_h(V)*(1 - h) - b_h(V)*h -\frac{ds}{dt} = \frac{1}{2}*(1 + \tanh(V/10))*(\frac{1 - s}{\tau_R} - \frac{s}{\tau_D})\]

where $jcn$ is any input to the blox. Note that this is a modified Hodgkin-Huxley formalism with an additional synaptic accumulation term. Synapses are added into the $jcn$ term by connecting the postsynaptic neuron's voltage to the presynaptic neuron's output:

\[jcn = w*s*(V_I - V)\]

where $w$ is the weight of the synapse and $V_I$ is the reversal potential of the inhibitory synapse.

Inputs:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • C: Membrane capacitance (defaults to 1.0).
  • g_Na: Sodium conductance (defaults to 35.0).
  • V_Na: Sodium reversal potential (defaults to 55.0).
  • g_K: Potassium conductance (defaults to 9.0).
  • V_K: Potassium reversal potential (defaults to -90.0).
  • g_L: Leak conductance (defaults to 0.1).
  • V_L: Leak reversal potential (defaults to -65.0).
  • I_ext: External current (defaults to 0.0).
  • τ_R: Rise time of synaptic conductance (defaults to 0.5).
  • τ_D: Decay time of synaptic conductance (defaults to 10.0).
source
Neuroblox.WilsonCowanType
WilsonCowan(name, namespace, τ_E, τ_I, a_E, a_I, c_EE, c_IE, c_EI, c_II, θ_E, θ_I, η)
+\frac{ds}{dt} = \frac{1}{2}*(1 + \tanh(V/10))*(\frac{1 - s}{\tau_R} - \frac{s}{\tau_D})\]

where $jcn$ is any input to the blox. Note that this is a modified Hodgkin-Huxley formalism with an additional synaptic accumulation term. Synapses are added into the $jcn$ term by connecting the postsynaptic neuron's voltage to the presynaptic neuron's output:

\[jcn = w*s*(V_I - V)\]

where $w$ is the weight of the synapse and $V_I$ is the reversal potential of the inhibitory synapse.

Inputs:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • C: Membrane capacitance (defaults to 1.0).
  • g_Na: Sodium conductance (defaults to 35.0).
  • V_Na: Sodium reversal potential (defaults to 55.0).
  • g_K: Potassium conductance (defaults to 9.0).
  • V_K: Potassium reversal potential (defaults to -90.0).
  • g_L: Leak conductance (defaults to 0.1).
  • V_L: Leak reversal potential (defaults to -65.0).
  • I_ext: External current (defaults to 0.0).
  • τ_R: Rise time of synaptic conductance (defaults to 0.5).
  • τ_D: Decay time of synaptic conductance (defaults to 10.0).
source
Neuroblox.WilsonCowanType
WilsonCowan(name, namespace, τ_E, τ_I, a_E, a_I, c_EE, c_IE, c_EI, c_II, θ_E, θ_I, η)
 
 Create a standard Wilson Cowan blox.
 The formal definition of this blox is:

\[\frac{dE}{dt} = \frac{-E}{\tau_E} + \frac{1}{1 + \text{exp}(-a_E*(c_{EE}*E - c_{IE}*I - \theta_E + \eta*(\sum{jcn}))} -\frac{dI}{dt} = \frac{-I}{\tau_I} + \frac{1}{1 + exp(-a_I*(c_{EI}*E - c_{II}*I - \theta_I)}\]

where $jcn$ is any input to the blox.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • Others: See equation for use.
source
Neuroblox.WinnerTakeAllBloxType
WinnerTakeAllBlox

Creates a winner-take-all local circuit found in neocortex, typically 5 pyramidal (excitatory) neurons send synapses to a single interneuron (inhibitory) and receive feedback inhibition from that interneuron.

source
LinearAlgebra.eigenMethod
function LinearAlgebra.eigen(M::Matrix{Dual{T, P, np}}) where {T, P, np}
+\frac{dI}{dt} = \frac{-I}{\tau_I} + \frac{1}{1 + exp(-a_I*(c_{EI}*E - c_{II}*I - \theta_I)}\]

where $jcn$ is any input to the blox.

Arguments:

  • name: Name given to ODESystem object within the blox.
  • namespace: Additional namespace above name if needed for inheritance.
  • Others: See equation for use.
source
Neuroblox.WinnerTakeAllBloxType
WinnerTakeAllBlox

Creates a winner-take-all local circuit found in neocortex, typically 5 pyramidal (excitatory) neurons send synapses to a single interneuron (inhibitory) and receive feedback inhibition from that interneuron.

source
LinearAlgebra.eigenMethod
function LinearAlgebra.eigen(M::Matrix{Dual{T, P, np}}) where {T, P, np}
 
 Dispatch of LinearAlgebra.eigen for dual matrices with complex numbers. Make the eigenvalue decomposition 
 amenable to automatic differentiation. To do so compute the analytical derivative of eigenvalues
@@ -79,10 +79,10 @@
 - `M`: matrix of type Dual of which to compute the eigenvalue decomposition. 
 
 Returns:
-- `Eigen(evals, evecs)`: eigenvalue decomposition returned as type LinearAlgebra.Eigen
source
Neuroblox.ARVTargetMethod

ARVTarget Time series data is bandpass filtered and then the power spectrum is computed for a given time interval (control bin), returned as the average value of the power spectral density within a certain frequency band ([lb, ub]).

source
Neuroblox.CDVTargetMethod

CDVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians. Circular difference is quantified as the angle of circular_location.

source
Neuroblox.PDVTargetMethod

PDVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians. Phase deviation is quantified as the angle difference between a given set of signals.

source
Neuroblox.PLVTargetMethod

PLVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians.

source
Neuroblox.ProtocolDBSMethod
ProtocolDBS(; name, namespace=nothing, frequency=130.0, amplitude=2.5,
+- `Eigen(evals, evecs)`: eigenvalue decomposition returned as type LinearAlgebra.Eigen
source
Neuroblox.ARVTargetMethod

ARVTarget Time series data is bandpass filtered and then the power spectrum is computed for a given time interval (control bin), returned as the average value of the power spectral density within a certain frequency band ([lb, ub]).

source
Neuroblox.CDVTargetMethod

CDVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians. Circular difference is quantified as the angle of circular_location.

source
Neuroblox.PDVTargetMethod

PDVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians. Phase deviation is quantified as the angle difference between a given set of signals.

source
Neuroblox.PLVTargetMethod

PLVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians.

source
Neuroblox.ProtocolDBSMethod
ProtocolDBS(; name, namespace=nothing, frequency=130.0, amplitude=2.5,
               pulse_width=0.066, offset=0.0, start_time=0.0, smooth=1e-4,
               pulses_per_burst=10, bursts_per_block=12, 
-              pre_block_time=200.0, inter_burst_time=200.0)

Create a deep brain stimulation (DBS) stimulus consisting of a block of pulse bursts.

Arguments:

  • name: Name given to ODESystem object within the blox
  • namespace: Additional namespace above name if needed for inheritance
  • frequency: Pulse frequency in Hz
  • amplitude: Pulse amplitude in arbitrary units
  • pulse_width: Duration of each pulse in ms
  • offset: Baseline value of the signal between pulses
  • start_time: Time delay before stimulation begins in ms
  • smooth: Smoothing parameter for pulse transitions, set to 0 for sharp transitions
  • pulsesperburst: Number of pulses in each burst
  • burstsperblock: Number of bursts in the block
  • preblocktime: Time before the block starts in ms
  • interbursttime: Time between bursts in ms

Returns a DBS stimulus blox that outputs a block of pulse bursts.

source
Neuroblox.addnontunableparamsMethod
function addnontunableparams(param, model)
+              pre_block_time=200.0, inter_burst_time=200.0)

Create a deep brain stimulation (DBS) stimulus consisting of a block of pulse bursts.

Arguments:

  • name: Name given to ODESystem object within the blox
  • namespace: Additional namespace above name if needed for inheritance
  • frequency: Pulse frequency in Hz
  • amplitude: Pulse amplitude in arbitrary units
  • pulse_width: Duration of each pulse in ms
  • offset: Baseline value of the signal between pulses
  • start_time: Time delay before stimulation begins in ms
  • smooth: Smoothing parameter for pulse transitions, set to 0 for sharp transitions
  • pulsesperburst: Number of pulses in each burst
  • burstsperblock: Number of bursts in the block
  • preblocktime: Time before the block starts in ms
  • interbursttime: Time between bursts in ms

Returns a DBS stimulus blox that outputs a block of pulse bursts.

source
Neuroblox.addnontunableparamsMethod
function addnontunableparams(param, model)
 
 Function adds parameters of a model that were not marked as tunable to a list of tunable parameters
 and respects the MTK ordering of parameters.
@@ -92,12 +92,12 @@
 - `sys`: MTK system
 
 Returns:
-- `completeparamlist`: complete parameter list of a system, including those that were not tagged as tunable
source
Neuroblox.bandpassfilterMethod

bandpassfilter takes in time series data and bandpass filters it. It has the following inputs: data: time series data lb: minimum cut-off frequency ub: maximum cut-off frequency fs: sampling frequency order: filter order

source
Neuroblox.boldsignalMethod

Arguments:

  • name: Name given to ODESystem object within the blox.
  • lnϵ : logarithm of ratio of intra- to extra-vascular signal

NB: the prefix ln of the variables ν, q as well as the parameters ϵ denotes their transformation into logarithmic space to enforce their positivity.

Citations:

  1. Stephan K E, Weiskopf N, Drysdale P M, Robinson P A, and Friston K J. Comparing Hemodynamic Models with DCM. NeuroImage 38, no. 3 (2007): 387–401. doi: 10.1016/j.neuroimage.2007.07.040
  2. Hofmann D, Chesebro A G, Rackauckas C, Mujica-Parodi L R, Friston K J, Edelman A, and Strey H H. Leveraging Julia's Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed, 2023. doi: 10.1101/2023.10.27.564407
source
Neuroblox.complexwaveletFunction

complexwavelet creates a complex morlet wavelet by windowing a complex sine wave with a Gaussian taper. The morlet wavelet is a special case of a bandpass filter in which the frequency response is Gaussian-shaped. Convolution with a complex wavelet is equivalent to performing a Hilbert transform of a bandpass filtered signal.

It has the following inputs: data: time series data dt : data sampling rate lb : lower bound wavelet frequency (in Hz) ub : upper bound wavelet frequency (in Hz) a : amplitude of the Gaussian taper, default is 1 n : number of wavelet cycles of the Gaussian taper, defines the trade-off between temporal precision and frequency precision larger n gives better frequency precision at the cost of temporal precision default is 6 Hz m : x-axis offset, default is 0 num_wavelets : number of wavelets to create, default is 5

And outputs: complex_wavelet : a family of complex morlet wavelets

source
Neuroblox.csd2marMethod

This function converts a cross-spectral density (CSD) into a multivariate auto-regression (MAR) model. It first transforms the CSD into its cross-correlation function (Wiener-Kinchine theorem) and then computes the MAR model coefficients. csd : cross-spectral density matrix of size MxN; M: number of samples, N: number of cross-spectral dimensions (number of variables squared) w : frequencies dt : time step size p : number of time steps of auto-regressive model

This function returns coeff : array of length p of coefficient matrices of size sqrt(N)xsqrt(N) noise_cov : noise covariance matrix

source
Neuroblox.csd_approxMethod
This function implements equation 2 of the spectral DCM paper, Friston et al. 2014 "A DCM for resting state fMRI".
+- `completeparamlist`: complete parameter list of a system, including those that were not tagged as tunable
source
Neuroblox.bandpassfilterMethod

bandpassfilter takes in time series data and bandpass filters it. It has the following inputs: data: time series data lb: minimum cut-off frequency ub: maximum cut-off frequency fs: sampling frequency order: filter order

source
Neuroblox.boldsignalMethod

Arguments:

  • name: Name given to ODESystem object within the blox.
  • lnϵ : logarithm of ratio of intra- to extra-vascular signal

NB: the prefix ln of the variables ν, q as well as the parameters ϵ denotes their transformation into logarithmic space to enforce their positivity.

Citations:

  1. Stephan K E, Weiskopf N, Drysdale P M, Robinson P A, and Friston K J. Comparing Hemodynamic Models with DCM. NeuroImage 38, no. 3 (2007): 387–401. doi: 10.1016/j.neuroimage.2007.07.040
  2. Hofmann D, Chesebro A G, Rackauckas C, Mujica-Parodi L R, Friston K J, Edelman A, and Strey H H. Leveraging Julia's Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed, 2023. doi: 10.1101/2023.10.27.564407
source
Neuroblox.complexwaveletFunction

complexwavelet creates a complex morlet wavelet by windowing a complex sine wave with a Gaussian taper. The morlet wavelet is a special case of a bandpass filter in which the frequency response is Gaussian-shaped. Convolution with a complex wavelet is equivalent to performing a Hilbert transform of a bandpass filtered signal.

It has the following inputs: data: time series data dt : data sampling rate lb : lower bound wavelet frequency (in Hz) ub : upper bound wavelet frequency (in Hz) a : amplitude of the Gaussian taper, default is 1 n : number of wavelet cycles of the Gaussian taper, defines the trade-off between temporal precision and frequency precision larger n gives better frequency precision at the cost of temporal precision default is 6 Hz m : x-axis offset, default is 0 num_wavelets : number of wavelets to create, default is 5

And outputs: complex_wavelet : a family of complex morlet wavelets

source
Neuroblox.csd2marMethod

This function converts a cross-spectral density (CSD) into a multivariate auto-regression (MAR) model. It first transforms the CSD into its cross-correlation function (Wiener-Kinchine theorem) and then computes the MAR model coefficients. csd : cross-spectral density matrix of size MxN; M: number of samples, N: number of cross-spectral dimensions (number of variables squared) w : frequencies dt : time step size p : number of time steps of auto-regressive model

This function returns coeff : array of length p of coefficient matrices of size sqrt(N)xsqrt(N) noise_cov : noise covariance matrix

source
Neuroblox.csd_approxMethod
This function implements equation 2 of the spectral DCM paper, Friston et al. 2014 "A DCM for resting state fMRI".
 Note that nomenclature is taken from SPM12 code and it does not seem to coincide with the spectral DCM paper's nomenclature. 
 For instance, Gu should represent the spectral component due to external input according to the paper. However, in the code this represents
 the hidden state fluctuations (which are called Gν in the paper).
 Gn in the code corresponds to Ge in the paper, i.e. the observation noise. In the code global and local components are defined, no such distinction
-is discussed in the paper. In fact the parameter γ, corresponding to local component is not present in the paper.
source
Neuroblox.get_dynamic_statesMethod
function get_dynamic_states(sys)
+is discussed in the paper. In fact the parameter γ, corresponding to local component is not present in the paper.
source
Neuroblox.get_dynamic_statesMethod
function get_dynamic_states(sys)
 
 Function extracts states from the system that are dynamic variables, 
 get also indices of external inputs (u(t)) and measurements (like bold(t))
@@ -106,7 +106,7 @@
 
 Returns:
 - `sts`: states/unknowns of the system that are neither external inputs nor measurements, i.e. these are the dynamic states
-- `idx`: indices of these states
source
Neuroblox.get_input_equationsMethod
Returns the equations for all input variables of a system, 
 assuming they have a form like : `sys.input_variable ~ ...`
 so only the input appears on the LHS.
 
@@ -116,20 +116,13 @@
 
 If blox isa AbstractComponent, it is assumed that it contains a `connector` field,
 which holds a `Connector` object with all relevant connections 
-from lower levels and this level.
source
Neuroblox.idftMethod

Plain implementation of idft because AD dispatch versions for ifft don't work still!

source
Neuroblox.idftMethod

Plain implementation of idft because AD dispatch versions for ifft don't work still!

source
Neuroblox.inner_namespaceofMethod
Returns the complete namespace EXCLUDING the outermost (highest) level.
 This is useful for manually preparing equations (e.g. connections, see Connector),
-that will later be composed and will automatically get the outermost namespace.
source
Neuroblox.learningrateMethod

This function computes learning rate. It has the following inputs: outcomes: vector of 1's and 0's for behavioral outcomes windows: number of windows to split the outcome data into And the following outputs: rate: the learning rate across each window

source
Neuroblox.mar2csdMethod

This function converts multivariate auto-regression (MAR) model parameters to a cross-spectral density (CSD). A : coefficients of MAR model, array of length p, each element contains the regression coefficients for that particular time-lag. Σ : noise covariance matrix of MAR p : number of time lags freqs : frequencies at which to evaluate the CSD sf : sampling frequency

This function returns: csd : cross-spectral density matrix of size MxN; M: number of samples, N: number of cross-spectral dimensions (number of variables squared)

source
Neuroblox.mar_mlMethod

Maximum likelihood estimator of a multivariate, or vector auto-regressive model. y : MxN Data matrix where M is number of samples and N is number of dimensions p : time lag parameter, also called order of MAR model return values mar["A"] : model parameters is a NxNxP tensor, i.e. one NxN parameter matrix for each time bin k ∈ {1,...,p} mar["Σ"] : noise covariance matrix

source
Neuroblox.matlab_normMethod
function matlab_norm(A, p)
-
-Simple helper function to implement the norm of a matrix that is equivalent to the one given in MATLAB for order=1, 2, Inf. 
-This is needed for the reproduction of the exact same results of SPM12.
-
-Arguments:
-- `A`: matrix
-- `p`: order of norm
source
Neuroblox.paramscopingMethod
function paramscoping(;tunable=true, kwargs...)
+that will later be composed and will automatically get the outermost namespace.
source
Neuroblox.learningrateMethod

This function computes learning rate. It has the following inputs: outcomes: vector of 1's and 0's for behavioral outcomes windows: number of windows to split the outcome data into And the following outputs: rate: the learning rate across each window

source
Neuroblox.mar2csdMethod

This function converts multivariate auto-regression (MAR) model parameters to a cross-spectral density (CSD). A : coefficients of MAR model, array of length p, each element contains the regression coefficients for that particular time-lag. Σ : noise covariance matrix of MAR p : number of time lags freqs : frequencies at which to evaluate the CSD sf : sampling frequency

This function returns: csd : cross-spectral density matrix of size MxN; M: number of samples, N: number of cross-spectral dimensions (number of variables squared)

source
Neuroblox.mar_mlMethod

Maximum likelihood estimator of a multivariate, or vector auto-regressive model. y : MxN Data matrix where M is number of samples and N is number of dimensions p : time lag parameter, also called order of MAR model return values mar["A"] : model parameters is a NxNxP tensor, i.e. one NxN parameter matrix for each time bin k ∈ {1,...,p} mar["Σ"] : noise covariance matrix

source
Neuroblox.paramscopingMethod
function paramscoping(;tunable=true, kwargs...)
 
 Scope arguments that are already a symbolic model parameter thereby keep the correct namespace 
 and make those that are not yet symbolic a symbol.
-Keyword arguments are used, because parameter definition require names, not just values.
source
Neuroblox.phase_cos_bloxMethod

phasecosblox is creating a cos with angular frequency ω and variable phase phaseinter has the following parameters: ω: angular frequency t: time phaseinter: a function that returns phase as a function of time and returns: the resulting value

Usage: phaseint = phaseinter(0:0.1:50,phasedata) phaseout(t) = phasecosblox(0.1,t,phaseint) which is now a function of time and can be used in an input blox you can also use the dot operator to calculate time-series signal = phaseout.(collect(0:0.01:50))

source
Neuroblox.phase_interMethod

phaseinter is creating a function that interpolates the phase data for any time given phaseinter has the following parameters: phaserange: a range, e.g. 0:0.1:50 which should reflect the time points of the data phasedata: phase at equidistant time points and returns: an function that returns an interpolated phase for t in range

source
Neuroblox.phase_sin_bloxMethod

phasesinblox is creating a sin with angular frequency ω and variable phase phaseinter has the following parameters: ω: angular frequency t: time phaseinter: a function that returns phase as a function of time and returns: the resulting value

Usage: phaseint = phaseinter(0:0.1:50,phasedata) phaseout(t) = phasesinblox(0.1,t,phaseint) which is now a function of time and can be used in an input blox you can also use the dot operator to calculate time-series signal = phaseout.(collect(0:0.01:50))

source
Neuroblox.random_initialsMethod

random_initials creates a vector of random initial conditions for an ODESystem that is composed of a list of blox. The function finds the initial conditions in the blox and then sets a random value in between range tuple given for that state.

It has the following inputs: odesys: ODESystem blox : list of blox

And outputs: u0 : Float64 vector of initial conditions

source
Neuroblox.setup_sDCMMethod
function setup_sDCM(data, stateevolutionmodel, initcond, csdsetup, priors, hyperpriors, indices)
+Keyword arguments are used, because parameter definition require names, not just values.
source
Neuroblox.phase_cos_bloxMethod

phasecosblox is creating a cos with angular frequency ω and variable phase phaseinter has the following parameters: ω: angular frequency t: time phaseinter: a function that returns phase as a function of time and returns: the resulting value

Usage: phaseint = phaseinter(0:0.1:50,phasedata) phaseout(t) = phasecosblox(0.1,t,phaseint) which is now a function of time and can be used in an input blox you can also use the dot operator to calculate time-series signal = phaseout.(collect(0:0.01:50))

source
Neuroblox.phase_interMethod

phaseinter is creating a function that interpolates the phase data for any time given phaseinter has the following parameters: phaserange: a range, e.g. 0:0.1:50 which should reflect the time points of the data phasedata: phase at equidistant time points and returns: an function that returns an interpolated phase for t in range

source
Neuroblox.phase_sin_bloxMethod

phasesinblox is creating a sin with angular frequency ω and variable phase phaseinter has the following parameters: ω: angular frequency t: time phaseinter: a function that returns phase as a function of time and returns: the resulting value

Usage: phaseint = phaseinter(0:0.1:50,phasedata) phaseout(t) = phasesinblox(0.1,t,phaseint) which is now a function of time and can be used in an input blox you can also use the dot operator to calculate time-series signal = phaseout.(collect(0:0.01:50))

source
Neuroblox.random_initialsMethod

random_initials creates a vector of random initial conditions for an ODESystem that is composed of a list of blox. The function finds the initial conditions in the blox and then sets a random value in between range tuple given for that state.

It has the following inputs: odesys: ODESystem blox : list of blox

And outputs: u0 : Float64 vector of initial conditions

source
Neuroblox.setup_sDCMMethod
function setup_sDCM(data, stateevolutionmodel, initcond, csdsetup, priors, hyperpriors, indices)
 
 Interface function to performs variational inference to fit model parameters to empirical cross spectral density.
 The current implementation provides a Variational Laplace fit (see function above `variationalbayes`).
@@ -149,15 +142,15 @@
 - `hyperpriors` : dataframe of parameters with the following columns:
 -- `Πλ_pr`      : prior precision matrix for λ hyperparameter(s)
 -- `μλ_pr`      : prior mean(s) for λ hyperparameter(s)
-- `indices`  : indices to separate model parameters from other parameters. Needed for the computation of AD gradient.
source
Neuroblox.spm_logdetMethod
function spm_logdet(M)
+- `indices`  : indices to separate model parameters from other parameters. Needed for the computation of AD gradient.
source
Neuroblox.spm_logdetMethod
function spm_logdet(M)
 
 SPM12 style implementation of the logarithm of the determinant of a matrix.
 
 Arguments:
-- `M`: matrix
source
Neuroblox.system_from_graphFunction
system_from_graph(g::MetaDiGraph, p=Num[]; name, simplify=true, graphdynamics=false, kwargs...)

Take in a MetaDiGraph g describing a network of neural structures (and optionally a vector of extra parameters p) and construct a System which can be used to construct various Problem types (i.e. ODEProblem) for use with DifferentialEquations.jl solvers.

If simplify is set to true (the default), then the resulting system will have structural_simplify called on it with any remaining keyword arguments forwared to structural_simplify. That is,

@named sys = system_from_graph(g; kwarg1=x, kwarg2=y)

is equivalent to

@named sys = system_from_graph(g; simplify=false)
-sys = structural_simplify(sys; kwarg1=x, kwarg2=y)

See the docstring for structural_simplify for information on which options it supports.

If graphdynamics=true (defaults to false), the output will be a GraphSystem from GraphDynamics.jl, and the kwargs will be sent to the GraphDynamics constructor instead of using ModelingToolkit.jl. The GraphDynamics.jl backend is typically significantly faster for large neural systems than the default backend, but is experimental and does not yet support all Neuroblox.jl features.

source
Neuroblox.system_from_graphFunction
system_from_graph(g::MetaDiGraph, p=Num[]; name, simplify=true, graphdynamics=false, kwargs...)

Take in a MetaDiGraph g describing a network of neural structures (and optionally a vector of extra parameters p) and construct a System which can be used to construct various Problem types (i.e. ODEProblem) for use with DifferentialEquations.jl solvers.

If simplify is set to true (the default), then the resulting system will have structural_simplify called on it with any remaining keyword arguments forwared to structural_simplify. That is,

@named sys = system_from_graph(g; kwarg1=x, kwarg2=y)

is equivalent to

@named sys = system_from_graph(g; simplify=false)
+sys = structural_simplify(sys; kwarg1=x, kwarg2=y)

See the docstring for structural_simplify for information on which options it supports.

If graphdynamics=true (defaults to false), the output will be a GraphSystem from GraphDynamics.jl, and the kwargs will be sent to the GraphDynamics constructor instead of using ModelingToolkit.jl. The GraphDynamics.jl backend is typically significantly faster for large neural systems than the default backend, but is experimental and does not yet support all Neuroblox.jl features.

source
Neuroblox.vecparamMethod
vecparam(param::OrderedDict)
 
 Function to flatten an ordered dictionary of model parameters and return a simple list of parameter values.
 
 Arguments:
-- `param`: dictionary of model parameters (may contain numbers and lists of numbers)
source
+- `param`: dictionary of model parameters (may contain numbers and lists of numbers)
source
diff --git a/dev/assets/Manifest.toml b/dev/assets/Manifest.toml index 2eb680c..d339496 100644 --- a/dev/assets/Manifest.toml +++ b/dev/assets/Manifest.toml @@ -280,9 +280,9 @@ version = "1.18.2+1" [[deps.ChainRules]] deps = ["Adapt", "ChainRulesCore", "Compat", "Distributed", "GPUArraysCore", "IrrationalConstants", "LinearAlgebra", "Random", "RealDot", "SparseArrays", "SparseInverseSubset", "Statistics", "StructArrays", "SuiteSparse"] -git-tree-sha1 = "bcffdcaed50d3453673b852f3522404a94b50fad" +git-tree-sha1 = "4312d7869590fab4a4f789e97bd82f0a04eaaa05" uuid = "082447d4-558c-5d27-93f4-14fc19e9eca2" -version = "1.72.1" +version = "1.72.2" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] @@ -582,9 +582,9 @@ version = "0.6.28" [[deps.DispatchDoctor]] deps = ["MacroTools", "Preferences"] -git-tree-sha1 = "453df2ce2aef7de59c69a56d31dcd2ec3384dd77" +git-tree-sha1 = "c204e42f63e0013d4446b9fe60fdad6edde7d8cf" uuid = "8d63f2c5-f18a-4cf2-ba9d-b3f60fc568c8" -version = "0.4.17" +version = "0.4.18" weakdeps = ["ChainRulesCore", "EnzymeCore"] [deps.DispatchDoctor.extensions] @@ -1441,9 +1441,9 @@ version = "2.40.2+2" [[deps.Libtiff_jll]] deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a" +git-tree-sha1 = "4ab7581296671007fc33f07a721631b8855f4b1d" uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.7.0+0" +version = "4.7.1+0" [[deps.Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -2973,9 +2973,9 @@ version = "0.4.1" [[deps.Unitful]] deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "01915bfcd62be15329c9a07235447a89d588327c" +git-tree-sha1 = "c0667a8e676c53d390a09dc6870b3d8d6650e2bf" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.21.1" +version = "1.22.0" weakdeps = ["ConstructionBase", "InverseFunctions"] [deps.Unitful.extensions] @@ -3037,9 +3037,9 @@ version = "1.1.42+0" [[deps.XZ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "15e637a697345f6743674f1322beefbc5dcd5cfc" +git-tree-sha1 = "beef98d5aad604d9e7d60b2ece5181f7888e2fd6" uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.6.3+2" +version = "5.6.4+0" [[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] @@ -3096,9 +3096,9 @@ version = "1.2.13+1" [[deps.Zstd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b" +git-tree-sha1 = "622cf78670d067c738667aaa96c553430b65e269" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.6+3" +version = "1.5.7+0" [[deps.isoband_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -3136,10 +3136,10 @@ uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" version = "1.6.45+0" [[deps.libsixel_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] -git-tree-sha1 = "7dfa0fd9c783d3d0cc43ea1af53d69ba45c447df" +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "libpng_jll"] +git-tree-sha1 = "1e53ffe8941ee486739f3c0cf11208c26637becd" uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" -version = "1.10.3+3" +version = "1.10.4+0" [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] diff --git a/dev/getting_started/index.html b/dev/getting_started/index.html index 36f0446..801da48 100644 --- a/dev/getting_started/index.html +++ b/dev/getting_started/index.html @@ -78,4 +78,4 @@ lines!(ax, sol.t, E1, label = "E") lines!(ax, sol.t, I1, label = "I") Legend(fig[1,2], ax) -figExample block output

[1] Wilson, H. R., & Cowan, J. D. (1972). Excitatory and inhibitory interactions in localized populations of model neurons. Biophysical journal, 12(1), 1-24.

[2] Destexhe, A., & Sejnowski, T. J. (2009). The Wilson–Cowan model, 36 years later. Biological cybernetics, 101(1), 1-2.


This page was generated using Literate.jl.

+figExample block output

[1] Wilson, H. R., & Cowan, J. D. (1972). Excitatory and inhibitory interactions in localized populations of model neurons. Biophysical journal, 12(1), 1-24.

[2] Destexhe, A., & Sejnowski, T. J. (2009). The Wilson–Cowan model, 36 years later. Biological cybernetics, 101(1), 1-2.


This page was generated using Literate.jl.

diff --git a/dev/index.html b/dev/index.html index 21da4e9..19c3276 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Neuroblox · Neuroblox

Neuroblox

About

Neuroblox.jl is designed for computational neuroscience and psychiatry applications. Our tools range from control circuit system identification to brain circuit simulations bridging scales from spiking neurons to fMRI-derived circuits, parameter-fitting models to neuroimaging data, interactions between the brain and other physiological systems, experimental optimization, and scientific machine learning.

Description

Neuroblox.jl is based on a library of modular computational building blocks (“blox”) in the form of systems of symbolic dynamic differential equations that can be combined to describe large-scale brain dynamics. Once a model is built, it can be simulated efficiently and fit electrophysiological and neuroimaging data. Moreover, the circuit behavior of multiple model variants can be investigated to aid in distinguishing between competing hypotheses. We employ ModelingToolkit.jl to describe the dynamical behavior of blox as symbolic (stochastic/delay) differential equations. Our libraries of modular blox consist of individual neurons (Hodgkin-Huxley, IF, QIF, LIF, etc.), neural mass models (Jansen-Rit, Wilson-Cowan, Lauter-Breakspear, Next Generation, microcanonical circuits etc.) and biomimetically-constrained control circuit elements. A GUI designed to be intuitive to neuroscientists allows researchers to build models that automatically generate high-performance systems of numerical ordinary/stochastic differential equations from which one can run stimulations with parameters fit to experimental data. Our benchmarks show that the increase in speed for simulation often exceeds a factor of 100 as compared to neural mass model implementation by the Virtual Brain (python) and similar packages in MATLAB. For parameter fitting of brain circuit dynamical models, we use Turing.jl to perform probabilistic modeling, including Hamilton-Monte-Carlo sampling and Automated Differentiation Variational Inference.

Licensing

Neuroblox is free for non-commerical and academic use. For full details of the license, please see the Neuroblox EULA. For commercial use, get in contact with sales@neuroblox.org.

+Neuroblox · Neuroblox

Neuroblox

About

Neuroblox.jl is designed for computational neuroscience and psychiatry applications. Our tools range from control circuit system identification to brain circuit simulations bridging scales from spiking neurons to fMRI-derived circuits, parameter-fitting models to neuroimaging data, interactions between the brain and other physiological systems, experimental optimization, and scientific machine learning.

Description

Neuroblox.jl is based on a library of modular computational building blocks (“blox”) in the form of systems of symbolic dynamic differential equations that can be combined to describe large-scale brain dynamics. Once a model is built, it can be simulated efficiently and fit electrophysiological and neuroimaging data. Moreover, the circuit behavior of multiple model variants can be investigated to aid in distinguishing between competing hypotheses. We employ ModelingToolkit.jl to describe the dynamical behavior of blox as symbolic (stochastic/delay) differential equations. Our libraries of modular blox consist of individual neurons (Hodgkin-Huxley, IF, QIF, LIF, etc.), neural mass models (Jansen-Rit, Wilson-Cowan, Lauter-Breakspear, Next Generation, microcanonical circuits etc.) and biomimetically-constrained control circuit elements. A GUI designed to be intuitive to neuroscientists allows researchers to build models that automatically generate high-performance systems of numerical ordinary/stochastic differential equations from which one can run stimulations with parameters fit to experimental data. Our benchmarks show that the increase in speed for simulation often exceeds a factor of 100 as compared to neural mass model implementation by the Virtual Brain (python) and similar packages in MATLAB. For parameter fitting of brain circuit dynamical models, we use Turing.jl to perform probabilistic modeling, including Hamilton-Monte-Carlo sampling and Automated Differentiation Variational Inference.

Licensing

Neuroblox is free for non-commerical and academic use. For full details of the license, please see the Neuroblox EULA. For commercial use, get in contact with sales@neuroblox.org.

diff --git a/dev/install/index.html b/dev/install/index.html index 59dc719..7bbe62a 100644 --- a/dev/install/index.html +++ b/dev/install/index.html @@ -7,4 +7,4 @@ Downloads.download("raw.githubusercontent.com/Neuroblox/NeurobloxDocsHost/refs/heads/main/Project.toml", joinpath(@__DIR__, "Project.toml")) Pkg.activate(@__DIR__) -Pkg.instantiate()

Please note that after running these commands Neuroblox will also be installed along with all other packages that are used in the tutorials.

NOTE: If you want to install only Neuroblox and not the other packages used in the tutorials you can run

Pkg.add("Neuroblox")
+Pkg.instantiate()

Please note that after running these commands Neuroblox will also be installed along with all other packages that are used in the tutorials.

NOTE: If you want to install only Neuroblox and not the other packages used in the tutorials you can run

Pkg.add("Neuroblox")
diff --git a/dev/objects.inv b/dev/objects.inv index 7bde10d..a21e654 100644 Binary files a/dev/objects.inv and b/dev/objects.inv differ diff --git a/dev/release_notes/index.html b/dev/release_notes/index.html index 3e834a7..3639073 100644 --- a/dev/release_notes/index.html +++ b/dev/release_notes/index.html @@ -1,2 +1,2 @@ -Release Notes · Neuroblox
+Release Notes · Neuroblox
diff --git a/dev/search_index.js b/dev/search_index.js index 5a51b78..7018aab 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"EditURL = \"parkinsons.jl\"","category":"page"},{"location":"tutorials/parkinsons/#Building-a-model-of-the-Basal-Ganglia-using-Neural-Mass-models","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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]. 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. In this tutorial, we will specifically cover:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"How to create a Jansen-Rit neural mass model in Neuroblox\nHow to create blocks with different default sets of parameters\nUsing symbolic hyperparameters during graph definition\nExtracting specific solutions states for plotting time series data\nRemaking an ODEProblem without needing to recompile an entire ODESystem\nPlotting basic spectrograms of simulations","category":"page"},{"location":"tutorials/parkinsons/#The-Jansen-Rit-Neural-Mass-Model","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"The Jansen-Rit Neural Mass Model","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"The Jansen-Rit model [2] is another popular neural mass model that, like the Wilson-Cowan model from the Getting Started, describes the average activity of neural populations. This resource is a good general introduction to the Jansen-Rit model if you'd like to read more. Each Jansen-Rit unit is defined by the following differential equations:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"beginalign*\nfracdxdt = y-frac2taux 10pt\nfracdydt = -fracxtau^2 + fracHtau left2lambda S(textstylesumjcn) - lambdaright\nendalign*","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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 sumjcn 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:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"S(x) = frac11 + textexp(-rx)","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"where r controls the steepness of the sigmoid, affecting the population's sensitivity to input.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"This model consists of four main components:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"A cortical column consisting of pyramidal cells PY, an excitatory interneuron population EI, and an inhibitory interneuron population II\nA striatal network consisting of two populations of medium spiny neurons, D1 and D2 (named for the different dopamine receptors), and fast-spiking interneurons FSI\nSubcortical structures including the subthalamic nucleus STN, the external segment of the globus pallidus GPE, and the internal segment of the globus pallidus GPI\nA thalamic oscillator Th","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"The connections between these oscillators are shown in the Neuroblox GUI below (blue = excitatory, red = inhibitory connections): (Image: Cortico-basal ganglia-thalamic-cortical loop structure)","category":"page"},{"location":"tutorials/parkinsons/#Setting-Up-the-Model","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Setting Up the Model","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"We're now going to set up this model using code. Let's start by importing the necessary libraries and defining our neural masses:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"using Neuroblox\nusing OrdinaryDiffEq\nusing CairoMakie","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"τ_factor = 1000; ## Convert time units from seconds to milliseconds\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Now we'll setup the neural masses. The values are all taken from the original paper [1], Table 1. 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). 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.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"# Create the cortical oscillators\n@named PY = JansenRit(cortical=true) ## default parameters cortical Jansen Rit blox\n@named EI = JansenRit(τ=0.01*τ_factor, H=20/τ_factor, λ=5, r=5)\n@named II = JansenRit(τ=2.0*τ_factor, H=60/τ_factor, λ=5, r=5)\n\n# Create the striatal oscillators\n@named D1 = JansenRit(τ=0.0022*τ_factor, H=20/τ_factor, λ=300, r=0.3)\n@named D2 = JansenRit(τ=0.0022*τ_factor, H=20/τ_factor, λ=300, r=0.3)\n@named FSI = JansenRit(τ=0.0022*τ_factor, H=20/τ_factor, λ=300, r=0.3)\n\n# Create the remaining subcortical oscillators\n@named STN = JansenRit(τ=0.01*τ_factor, H=20/τ_factor, λ=500, r=0.1)\n@named GPE = JansenRit(cortical=false) ## default parameters subcortical Jansen Rit blox\n@named GPI = JansenRit(cortical=false) ## default parameters subcortical Jansen Rit blox\n@named Th = JansenRit(τ=0.002*τ_factor, H=10/τ_factor, λ=20, r=5);\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/#Building-the-Circuit","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building the Circuit","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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. 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. 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. 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 in Julia, which you should look at if you want to build more general models.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"g = MetaDiGraph() ## define an empty graph\n\nparams = @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\n\n# Create connections\n\n# thalamocortical connection\nadd_edge!(g, Th => EI; weight = C_BGTh➡Cor)\n\n# remaining cortical → subcortical connections\nadd_edge!(g, PY => STN; weight = C_Cor➡BGTh)\nadd_edge!(g, PY => D1; weight = C_BGTh➡Cor)\nadd_edge!(g, PY => D2; weight = C_BGTh➡Cor)\nadd_edge!(g, PY => FSI; weight = C_BGTh➡Cor)\n\n# basal ganglia ↔ thalamus connections\nadd_edge!(g, STN => GPE; weight = C_BGTh)\nadd_edge!(g, STN => GPI; weight = C_BGTh)\nadd_edge!(g, GPE => STN; weight = -0.5*C_BGTh)\nadd_edge!(g, GPE => GPE; weight = -0.5*C_BGTh)\nadd_edge!(g, GPE => GPI; weight = -0.5*C_BGTh)\nadd_edge!(g, GPE => FSI; weight = -0.5*C_BGTh)\nadd_edge!(g, FSI => D1; weight = -0.5*C_BGTh)\nadd_edge!(g, FSI => D2; weight = -0.5*C_BGTh)\nadd_edge!(g, FSI => FSI; weight = -0.5*C_BGTh)\nadd_edge!(g, D1 => D1; weight = -0.5*C_BGTh)\nadd_edge!(g, D1 => D2; weight = -0.5*C_BGTh)\nadd_edge!(g, D1 => GPI; weight = -0.5*C_BGTh)\nadd_edge!(g, D2 => D2; weight = -0.5*C_BGTh)\nadd_edge!(g, D2 => D1; weight = -0.5*C_BGTh)\nadd_edge!(g, D2 => GPE; weight = -0.5*C_BGTh)\nadd_edge!(g, GPI => Th; weight = -0.5*C_BGTh)\n\n# corticocortical connections\nadd_edge!(g, PY => EI; weight = 6*C_Cor)\nadd_edge!(g, PY => II; weight = 1.5*C_Cor)\nadd_edge!(g, EI => PY; weight = 4.8*C_Cor)\nadd_edge!(g, II => PY; weight = -1.5*C_Cor)\nadd_edge!(g, II => II; weight = -3.3*C_Cor);\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/#Creating-the-Model","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Creating the Model","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Let's build the complete model:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"@named final_system = system_from_graph(g);\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"This creates a differential equations system from our graph representation using ModelingToolkit and symbolically simplifies it for efficient computation.","category":"page"},{"location":"tutorials/parkinsons/#Simulating-the-Model","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Simulating the Model","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Lastly, we create the ODEProblemfor our system, select an algorithm, in this caseTsit5()` (see discussion in the previous tutorial about solver choices if you're interested), and simulate 1 second of brain activity.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"sim_dur = 1000.0 ## Simulate for 1 second\nprob = ODEProblem(final_system, [], (0.0, sim_dur)) ## Create the problem to solve\nsol = solve(prob, Tsit5(), saveat=0.1); ## Solve the problem and save every 0.1ms\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/#Visualizing-the-results","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Visualizing the results","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Let's interrogate the solution to see what we have. For the purposes of this tutorial, we'll focus on the striatal oscillations. In this simple model, we should see relatively sharp on/off transitions in the striatal populations. To test this, let's use Symbolic Indexing to access the states we're interested in: the y state of the D1 neuron population.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"idx_func = ModelingToolkit.getu(sol, D1.system.y); ## gets the state index of the D1 neuron population in the solution object\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Now use this indexing function to plot the solution in a Makie plot (read more about Makie in the docs).","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"fig = Figure() ## create the figure\nax = Axis(fig[1, 1], xlabel=\"Time (ms)\", ylabel=\"y voltage (arbitrary units)\", title=\"D1 population membrane voltage\") ## define one axis for the figure\nlines!(ax, sol.t, idx_func(sol), color = :blue) ## plot the time series of the D1 neuron population\ndisplay(fig) ## display the figure","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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. To do this, we'll use the remake function to create a new ODEProblem with a longer simulation time without rebuilding the entire system.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"sim_dur = 10000.0 ## Simulate for 10 seconds\nprob = remake(prob, tspan = (0.0, sim_dur)) ## Remake the ODEProblem with the new simulation time\nsol = solve(prob, Tsit5(), saveat=0.1); ## Solve the new problem\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"powerspectrumplot(D1, sol, state = \"y\", ## specify the block to plot, the solution object, and the state of the block to plot\n method = welch_pgram, window = hanning, ## power spectrum estimation method\n title=\"D1 Population Power Spectrum\")","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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.","category":"page"},{"location":"tutorials/parkinsons/#Further-Exploration","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Further Exploration","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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 C_Cor=60, C_BGTh=60, C_CorBGTh=5, and C_BGThCor=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. 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! If you're inspired to continue experimenting, here are a couple other ideas:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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.\nTry changing the lengths of the simulation and the sampling frequency. How does this affect the power spectrum?","category":"page"},{"location":"tutorials/parkinsons/#References","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"References","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"[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 [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.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"This page was generated using Literate.jl.","category":"page"},{"location":"install/#Installing-Neuroblox","page":"How to install","title":"Installing Neuroblox","text":"","category":"section"},{"location":"install/","page":"How to install","title":"How to install","text":"To install Neuroblox.jl, you need to first add the JuliaHubRegistry your list of registries that julia checks for available package ","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"using Pkg\nPkg.add(\"PkgAuthentication\")\nusing PkgAuthentication\nPkgAuthentication.install(\"juliahub.com\")\nPkg.Registry.add()","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"The next step is to install Neuroblox from the JuliaHubRegistry. It is also useful to install some other packages that are commonly used with Neuroblox. These packages are used in the tutorials of the next section. We have included Neuroblox and these other packages into a single Project.toml file which you can download and then use it to activate a new environment where all the necessary packages will be installed. To do this first choose a folder where you want this environment to be generated in and then run ","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"using Downloads\n\nDownloads.download(\"raw.githubusercontent.com/Neuroblox/NeurobloxDocsHost/refs/heads/main/Project.toml\", joinpath(@__DIR__, \"Project.toml\"))\nPkg.activate(@__DIR__)\nPkg.instantiate()","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"Please note that after running these commands Neuroblox will also be installed along with all other packages that are used in the tutorials.","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"NOTE: If you want to install only Neuroblox and not the other packages used in the tutorials you can run Pkg.add(\"Neuroblox\")","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"EditURL = \"ping_network.jl\"","category":"page"},{"location":"tutorials/ping_network/#Pyramidal-Interneuron-Gamma-network","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"","category":"section"},{"location":"tutorials/ping_network/#Introduction","page":"Pyramidal-Interneuron Gamma network","title":"Introduction","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"This tutorial provides a simple example of how to use the Neuroblox package to simulate a pyramidal-interneuron gamma (PING) network. These networks are generally useful in modeling cortical oscillations and are used in a variety of contexts. This particular example is based on Börgers, Epstein, and Kopell [1] and is a simple example of how to replicate their initial network in Neuroblox. This tutorial will cover the following concepts:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Creating populations of neurons for rapid simulation\nCreating input current sources drawn from user-specified distributions\nCreating a directed graph of connections between neurons\nCompiling the graph into a system of ODEs and simulating the network\nVisualizing the results with raster plots","category":"page"},{"location":"tutorials/ping_network/#Conceptual-definition","page":"Pyramidal-Interneuron Gamma network","title":"Conceptual definition","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"The PING network is a simple model of a cortical network that consists of two populations of neurons: excitatory and inhibitory. We omit the detailed equations of the neurons here, but note they are Hodgkin-Huxley-like equations with a few modifications. Excitatory neurons are reduced Traub-Miles cells [2] and inhibitory neurons are Wang-Buzasaki cells [3]. Both follow Hodgkin-Huxley formalism, i.e., the membrane voltage is governed by the sum of the currents through the sodium, potassium, and leak channels, along with external drive, such that:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"beginequation*\nC fracdVdt = g_textNa m^3 h (V_textNa - V) + g_textK n^4 (V_textK - V) + g_textL (V_textL - V) + I_textext\nendequation*","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"For full details of the model, see Eq. 12-14 on p. 7 of the SI Appendix of Börgers et al. [1]. Here is a visual representation of the network structure and which neurons receive the driving input: (Image: PING network structure)","category":"page"},{"location":"tutorials/ping_network/#Model-setup","page":"Pyramidal-Interneuron Gamma network","title":"Model setup","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"This section sets up the model parameters and the network structure. The network consists of 200 neurons: 40 driven excitatory neurons, 120 other excitatory neurons, and 40 inhibitory neurons. The network is set up as a directed graph with excitatory neurons driving inhibitory neurons and vice versa, with self-inhibition but not self-excitation present.","category":"page"},{"location":"tutorials/ping_network/#Import-the-necessary-packages","page":"Pyramidal-Interneuron Gamma network","title":"Import the necessary packages","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Reasons for each non-Neuroblox package are given in the comments after each.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"using Neuroblox\nusing OrdinaryDiffEq ## to build the ODE problem and solve it, gain access to multiple solvers from this\nusing Distributions ## for statistical distributions\nusing Random ## for random number generation\nusing CairoMakie ## for plotting","category":"page"},{"location":"tutorials/ping_network/#Initialization","page":"Pyramidal-Interneuron Gamma network","title":"Initialization","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Set the random seed to reproduce the plots as shown here exactly. If you want to probe how random variability changes the network, simply omit this line.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Random.seed!(42);\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Setup the hyperparameters for the PING network simulation. The comments note where these parameters are taken from in the Börgers et al. paper [1] or if they were manually tuned for this particular simulation.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"μ_E = 0.8 ## mean of the excitatory neurons' external current, manually tuned from the value on p. 8 of the Appendix\nσ_E = 0.15 ## standard deviation of the excitatory neurons' external current, given on p. 8 of the Appendix\nμ_I = 0.8 ## mean of the inhibitory neurons' external current, given on p. 9 of the Appendix\nσ_I = 0.08 ## standard deviation of the inhibitory neurons' external current, given on p. 9 of the Appendix\n\nNE_driven = 40 ## number of driven excitatory neurons, given on p. 8 of the Appendix. Note all receive constant rather than half stochastic drives.\nNE_other = 120 ## number of non-driven excitatory neurons, given in the Methods section\nNI_driven = 40 ## number of inhibitory neurons (all driven), given in the Methods section\nN_total = NE_driven + NE_other + NI_driven ## total number of neurons in the network\n\nN = N_total ## convenience redefinition to improve the readability of the connection weights\ng_II = 0.2 ## inhibitory-inhibitory connection weight, given on p. 8 of the Appendix\ng_IE = 0.6 ## inhibitory-excitatory connection weight, given on p. 8 of the Appendix\ng_EI = 0.8; ## excitatory-inhibitory connection weight, manually tuned from values given on p. 8 of the Appendix\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Finally, setup the driving currents. All neurons receive a base external current, and the inhibitory and driven excitatory populations receive a second external stimulus current. The undriven excitatory neurons receive a small addition to the base current in lieu of the stochastic current in the original implementation. There is also an external inhibitory bath for the inhibitory neurons - for the importance of this bath see the SI Appendix of Börgers et al. [1]. These currents are specified as distributions using the syntax from Distributions.jl. The advantage to this is that a distribution can be given to a call of rand() and the random number will be drawn from the specified distribution. We'll use this call during the neuron creation step below.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"I_base = Normal(0, 0.1) ## base external current for all neurons\nI_driveE = Normal(μ_E, σ_E) ## External current for driven excitatory neurons\nI_driveI = Normal(μ_I, σ_I) ## External current for driven inhibitory neurons\nI_undriven = Normal(0, 0.4) ## Additional noise current for undriven excitatory neurons. Manually tuned.\nI_bath = -0.7; ## External inhibitory bath for inhibitory neurons - value from p. 11 of the SI Appendix\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/#Creating-a-network-in-Neuroblox","page":"Pyramidal-Interneuron Gamma network","title":"Creating a network in Neuroblox","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Creating and running a network of neurons in Neuroblox consists of three steps: defining the neurons, defining the graph of connections between the neurons, and simulating the system represented by the graph.","category":"page"},{"location":"tutorials/ping_network/#Define-the-neurons","page":"Pyramidal-Interneuron Gamma network","title":"Define the neurons","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"The neurons from Börgers et al. [1] are implemented in Neuroblox as PINGNeuronExci and PINGNeuronInhib. We can specify their initial current drives and create the neurons as follows:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"exci_driven = [PINGNeuronExci(name=Symbol(\"ED$i\"), I_ext=rand(I_driveE) + rand(I_base)) for i in 1:NE_driven] ## In-line loop to create the driven excitatory neurons, named ED1, ED2, etc.\nexci_other = [PINGNeuronExci(name=Symbol(\"EO$i\"), I_ext=rand(I_base) + rand(I_undriven)) for i in 1:NE_other] ## In-line loop to create the undriven excitatory neurons, named EO1, EO2, etc.\nexci = [exci_driven; exci_other] ## Concatenate the driven and undriven excitatory neurons into a single vector for convenience\ninhib = [PINGNeuronInhib(name=Symbol(\"ID$i\"), I_ext=rand(I_driveI) + rand(I_base) + I_bath) for i in 1:NI_driven]; ## In-line loop to create the inhibitory neurons, named ID1, ID2, etc.\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/#Define-the-graph-of-network-connections","page":"Pyramidal-Interneuron Gamma network","title":"Define the graph of network connections","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"This portion illustrates how we go about creating a network of neuronal connections.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"g = MetaDiGraph() ## Initialize the graph\n\n# Add the E -> I and I -> E connections\nfor ne ∈ exci\n for ni ∈ inhib\n add_edge!(g, ne => ni; weight=g_EI/N)\n add_edge!(g, ni => ne; weight=g_IE/N)\n end\nend\n\n# Add the I -> I connections\nfor ni1 ∈ inhib\n for ni2 ∈ inhib\n add_edge!(g, ni1 => ni2; weight=g_II/N);\n end\nend","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"note: Alternative graph creation\nIf you are creating a very large network of neurons, it may be more efficient to add all of the nodes first and then all of the edges via an adjacency matrix. To illustrate this, here is an alternative to the previous block that will initialize the same graph. Just uncomment this code and use it to replace the code from the section above. julia g = MetaDiGraph() ## Initialize the graph add_blox!.(Ref(g), [exci; inhib]) ## Add all the neurons to the graph adj = zeros(N_total, N_total) ## Initialize the adjacency matrix for i ∈ 1:NE_driven + NE_other for j ∈ 1:NI_driven adj[i, NE_driven + NE_other + j] = g_EI/N adj[NE_driven + NE_other + j, i] = g_IE/N end end for i ∈ 1:NI_driven for j ∈ 1:NI_driven adj[NE_driven + NE_other + i, NE_driven + NE_other + j] = g_II/N end end create_adjacency_edges!(g, adj)","category":"page"},{"location":"tutorials/ping_network/#Simulate-the-network","page":"Pyramidal-Interneuron Gamma network","title":"Simulate the network","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Now that we have the neurons and the graph, we can simulate the network. We use the system_from_graph function to create a system of ODEs from the graph and then solve it using the DifferentialEquations.jl package, but for performance scaling reasons we will use the experimental option graphdynamics=true which uses a separate compilation backend called GraphDynamics.jl. The GraphDynamics.jl backend is still experimental, and may not yet support all of the standard Neuroblox features, such as those seen in the Spectral DCM tutorial. We choose to solve this system using the Tsit5() solver. If you're coming from Matlab, this is a more efficient solver analogous to ode45. It's a good first try for systems that aren't really stiff. If you want to try other solvers, we'd recommend trying with Vern7() (higher precision but still efficient). If you're really interested in solver choices, one of the great things about Julia is the wide variety of solvers available.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"tspan = (0.0, 300.0) ## Time span for the simulation - run for 300ms to match the Börgers et al. [1] Figure 1.\n@named sys = system_from_graph(g, graphdynamics=true) ## Use GraphDynamics.jl otherwise this can be a very slow simulation\nprob = ODEProblem(sys, [], tspan) ## Create the problem to solve\nsol = solve(prob, Tsit5(), saveat=0.1); ## Solve the problem and save at 0.1ms resolution.\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/#Plotting-the-results","page":"Pyramidal-Interneuron Gamma network","title":"Plotting the results","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Now that we have a whole simulation, let's plot the results and see how they line up with the original figures. We're looking to reproduce the dynamics shown in Figure 1 of Börgers et al. [1]. To create raster plots in Neuroblox for the excitatory and inhibitory populations, it is as simple as:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"fig = Figure()\nrasterplot(fig[1,1], exci, sol; threshold=20.0, title=\"Excitatory Neurons\")\nrasterplot(fig[2,1], inhib, sol; threshold=20.0, title=\"Inhibitory Neurons\")\nfig","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"The upper panel should show the dynamics in Figure 1.C, with a clear population of excitatory neurons firing together from the external driving current, and the other excitatory neurons exhibiting more stochastic bursts. The lower panel should show the dynamics in Figure 1.A, with the inhibitory neurons firing in a more synchronous manner than the excitatory neurons.","category":"page"},{"location":"tutorials/ping_network/#Conclusion","page":"Pyramidal-Interneuron Gamma network","title":"Conclusion","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"And there you have it! A complete PING demonstration that reproduces the dynamics of a published paper in a matter of 30 seconds, give or take. Have fun making your own!","category":"page"},{"location":"tutorials/ping_network/#For-further-exploration","page":"Pyramidal-Interneuron Gamma network","title":"For further exploration","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"If you want to explore the PING network further, you can try the following:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"You might notice that the excitatory and inhibitory populations become slightly desynchronized by the end of the simulation, unlike in the original paper. This is because of slight differences in how we implement the excitatory drive and inhibitory bath, which adjusts the overall E/I balance. Try increasing the inhibitory bath or decreasing the percentage of excitatory neurons that receive input and see how this affects the synchrony!\nThis tutorial is written entirely using built-in blocks for the excitatory and inhibitory neurons. If you want to explore the details, try typing PINGNeuronExci or PINGNeuronInhib in your Julia REPL to see the full details of the blocks. If you really want to dig into the details, type edit PINGNeuronExci() to open the source code and see how the equations are written.","category":"page"},{"location":"tutorials/ping_network/#References","page":"Pyramidal-Interneuron Gamma network","title":"References","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Börgers C, Epstein S, Kopell NJ. Gamma oscillations mediate stimulus competition and attentional selection in a cortical network model. Proc Natl Acad Sci U S A. 2008 Nov 18;105(46):18023-8. DOI: 10.1073/pnas.0809511105.\nTraub, RD, Miles, R. Neuronal Networks of the Hippocampus. Cambridge University Press, Cambridge, UK, 1991. DOI: 10.1017/CBO9780511895401\nWang, X-J, Buzsáki, G. Gamma oscillation by synaptic inhibition in a hippocampal interneuronal network model. J. Neurosci., 16:6402–6413, 1996. DOI: 10.1523/JNEUROSCI.16-20-06402.1996","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"This page was generated using Literate.jl.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"EditURL = \"basal_ganglia.jl\"","category":"page"},{"location":"tutorials/basal_ganglia/#Basal-Ganglia-Model-and-Parkinson's-Disease-Simulation","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"","category":"section"},{"location":"tutorials/basal_ganglia/#Introduction","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Introduction","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"This tutorial demonstrates how to build and simulate a basal ganglia model using Neuroblox, based on the work of Adam et al. (2022). We'll explore the model's behavior in both normal and Parkinsonian conditions, showcasing the emergence of pathological beta oscillations characteristic of Parkinson's disease.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"(Image: Full basal ganglia model in baseline condition)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"In previous tutorials, we explored building neural circuits from individual neuron bloxs and creating networks using neural mass bloxs. Here, we'll demonstrate how Neuroblox enables modeling of complex brain structures using specialized composite bloxs that encapsulate entire neural populations. These composite bloxs represent distinct neuronal populations within the basal ganglia, each containing multiple Hodgkin-Huxley neurons with their characteristic properties and intrinsic connectivity patterns.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"We'll start with simple components and gradually build up to the full basal ganglia circuit, demonstrating how to analyze the results at each stage.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"using Neuroblox\nusing StochasticDiffEq ## For building and solving differential equations problems\nusing CairoMakie ## For plotting\nusing Random ## For setting a random seed\n\nRandom.seed!(123) ## Set a random seed for reproducibility","category":"page"},{"location":"tutorials/basal_ganglia/#Isolated-MSN-network-in-baseline-condition","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Isolated MSN network in baseline condition","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"We'll start by simulating an isolated network of Medium Spiny Neurons (MSNs)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Blox definition","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"N_MSN = 100 ## number of Medium Spiny Neurons\n@named msn = Striatum_MSN_Adam(N_inhib = N_MSN)\nsys = structural_simplify(get_system(msn))\n\n# Check the system's variables (100 neurons, each with associated currents)\nunknowns(sys)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Create and solve the SDE problem","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"# Define simulation parameters\ntspan = (0.0, 2000.0) ## simulation time span [ms]\ndt = 0.05 ## time step for solving and saving [ms]\n\n# Create a stochastic differential equation problem and use the RKMil method to solve it\nprob = SDEProblem(sys, [], tspan, [])\nsol = solve(prob, RKMil(), dt = dt, saveat = dt);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Plot voltage of a single neuron","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"v = voltage_timeseries(msn, sol)\nfig = Figure()\nax = Axis(fig[1,1]; xlabel = \"Time (ms)\", ylabel = \"Voltage (mv)\")\nlines!(ax, sol.t, v[:, 1])\nfig ## to display the figure","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Plot mean field","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"meanfield(msn, sol, title = \"Mean Field Potential\")","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute firing rate, discarding the first 200 ms","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fr = firing_rate(msn, sol, threshold=-35, transient=200)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Create a raster plot","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"rasterplot(msn, sol, threshold = -35, title = \"Neuron's Spikes - Mean Firing Rate: $(round(fr[1], digits=2)) spikes/s\")","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute and plot the power spectrum of the GABAa current","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fig = Figure(size = (1500, 500))\n\npowerspectrumplot(fig[1,1], msn, sol, state = \"G\",\n title = \"FFT with no window\")\n\npowerspectrumplot(fig[1,2], msn, sol, state = \"G\",\n method = welch_pgram, window = hanning,\n title = \"Welch's method + Hanning window\")\nfig","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"We can leverage parallel computing using EnsembleProblem() to run multiple simulations simultaneously. This allows us to evaluate model outputs with multiple realizations of random physiological noise while utilizing all available computational threads (default) or processes.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"ens_prob = EnsembleProblem(prob)\nens_sol = solve(ens_prob, RKMil(), dt=dt, saveat=dt, trajectories = 3);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute average power spectrum","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"powerspectrumplot(msn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n title = \"Welch's method + Hanning window + Ensemble\")","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Note the peak at ~12 Hz in the MSN population's activity, representing an emergent beta-band oscillation characteristic of this circuit. We'll explore how this rhythm is altered by FSI inhibition next.","category":"page"},{"location":"tutorials/basal_ganglia/#Core-striatal-network:-MSN-FSI","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Core striatal network: MSN + FSI","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Now we'll add Fast-Spiking Interneurons (FSIs) to our model","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"global_ns = :g ## global name for the circuit. All components should be inside this namespace.\n\nN_FSI = 50 ## number of Fast Spiking Interneurons\n@named msn = Striatum_MSN_Adam(namespace = global_ns, N_inhib = N_MSN)\n@named fsi = Striatum_FSI_Adam(namespace = global_ns, N_inhib = N_FSI)\n\nḡ_FSI_MSN = 0.6 ## maximal conductance for FSI to MSN synapses [mS/cm^-2]\ndensity_FSI_MSN = 0.15 ## fraction of FSIs connecting to the MSN population\nweight_FSI_MSN = ḡ_FSI_MSN / (N_FSI * density_FSI_MSN) ## normalized synaptic weight\n\ng = MetaDiGraph()\nadd_edge!(g, fsi => msn, weight = weight_FSI_MSN, density = density_FSI_MSN)\n\n@named sys = system_from_graph(g)\nprob = SDEProblem(sys, [], tspan, [])\nens_prob = EnsembleProblem(prob)\nens_sol = solve(ens_prob, RKMil(), dt=dt, saveat = dt, trajectories = 3);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute firing rates for comparison","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fr_msn = firing_rate(msn, ens_sol[1], threshold=-35, transient=200)\nfr_fsi = firing_rate(fsi, ens_sol[1], threshold=-35, transient=200)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Let's see their raster plots and power spectra","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fig = Figure(size = (1000, 800))\nrasterplot(fig[1,1], msn, ens_sol[1], threshold = -35, title = \"MSN - Mean Firing Rate: $(round(fr_msn[1], digits=2)) spikes/s\")\nrasterplot(fig[1,2], fsi, ens_sol[1], threshold = -35, title = \"FSI - Mean Firing Rate: $(round(fr_fsi[1], digits=2)) spikes/s\")\n\npowerspectrumplot(fig[2,1], msn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims= (-35, 15),\n xlims= (8, 100))\n\npowerspectrumplot(fig[2,2], fsi, ens_sol, state = \"G\",\n method=welch_pgram, window=hanning,\n ylims= (-35, 15),\n xlims= (8, 100))\nfig","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"FSIs exhibit a peak in gamma frequencies. Their inhibition onto MSNs suppresses the low beta-band activity seen in isolated MSN populations, without reducing MSN firing rates. This spectral shift reflects a change in MSN spiking dynamics under FSI influence, rather than a decrease in overall activity.","category":"page"},{"location":"tutorials/basal_ganglia/#Full-basal-ganglia-model-in-baseline-condition","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Full basal ganglia model in baseline condition","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Now we'll add the GPe and STN to complete the full basal ganglia model","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"N_GPe = 80 ## number of GPe neurons\nN_STN = 40 ## number of STN neurons\n\n@named gpe = GPe_Adam(namespace = global_ns, N_inhib = N_GPe)\n@named stn = STN_Adam(namespace = global_ns, N_exci = N_STN)\n\nḡ_MSN_GPe = 2.5 ## maximal conductance for MSN to GPe synapses [mS/cm^-2]\nḡ_GPe_STN = 0.3 ## maximal conductance for GPe to STN synapses [mS/cm^-2]\nḡ_STN_FSI = 0.165 ## maximal conductance for STN to FSI synapses [mS/cm^-2]\n\ndensity_MSN_GPe = 0.33 ## fraction of MSNs connecting to the GPe population\ndensity_GPe_STN = 0.05 ## fraction of GPe neurons connecting to the STN population\ndensity_STN_FSI = 0.1 ## fraction of STN neurons connecting to the FSI population\n\nweight_MSN_GPe = ḡ_MSN_GPe / (N_MSN * density_MSN_GPe)\nweight_GPe_STN = ḡ_GPe_STN / (N_GPe * density_GPe_STN)\nweight_STN_FSI = ḡ_STN_FSI / (N_STN * density_STN_FSI)\n\ng = MetaDiGraph()\nadd_edge!(g, fsi => msn, weight = weight_FSI_MSN, density = density_FSI_MSN)\nadd_edge!(g, msn => gpe, weight = weight_MSN_GPe, density = density_MSN_GPe)\nadd_edge!(g, gpe => stn, weight = weight_GPe_STN, density = density_GPe_STN)\nadd_edge!(g, stn => fsi, weight = weight_STN_FSI, density = density_STN_FSI)\n\n@named sys = system_from_graph(g)\nprob = SDEProblem(sys, [], tspan, [])\nens_prob = EnsembleProblem(prob)\nens_sol = solve(ens_prob, RKMil(), dt=dt, saveat = dt, trajectories = 3);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute and plot power spectra for all components","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fig = Figure(size = (1600, 450))\n\npowerspectrumplot(fig[1,1], msn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"MSN (Baseline)\")\n\npowerspectrumplot(fig[1,2], fsi, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"FSI (Baseline)\")\n\npowerspectrumplot(fig[1,3], gpe, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"GPe (Baseline)\")\n\npowerspectrumplot(fig[1,4], stn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"STN (Baseline)\")\n\nfig","category":"page"},{"location":"tutorials/basal_ganglia/#Full-basal-ganglia-model-in-Parkinson's-condition","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Full basal ganglia model in Parkinson's condition","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Finally, we'll adjust the model parameters to simulate Parkinson's disease conditions","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"(Image: Full basal ganglia model in Parkinsonian conditions)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"The key changes from baseline to Parkinsonian conditions are:","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"For MSNs:\nIncreased background excitation (I_bg) to 1.2519 μA·cm^-2\nDecreased maximal conductance for M-current (G_M) to 1.2 mS·cm^-2\nFor FSIs:\nDecreased background excitation (I_bg) to 4.511 μA·cm^-2\nDecreased maximal conductance of FSI-MSN projection (ḡ_FSI_MSN) by 20% to 0.48 mS·cm^-2, due to increased cholinergic tone\nDecreased maximal conductance of FSI-FSI projection (weight) to 0.2 mS·cm^-2\nDecreased electrical conductance (g_elec) to 0.075","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"These changes reflect the loss of dopamine and increase in cholinergic tone characteristic of Parkinson's disease.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Create bloxs with Parkinsonian parameters","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"@named msn = Striatum_MSN_Adam(namespace = global_ns, N_inhib = N_MSN, I_bg = 1.2519*ones(N_MSN), G_M = 1.2)\n@named fsi = Striatum_FSI_Adam(namespace = global_ns, N_inhib = N_FSI, I_bg = 4.511*ones(N_FSI), weight = 0.2, g_weight = 0.075)\n\nḡ_FSI_MSN = 0.48 ## decreased maximal conductance of FSI-MSN projection [mS/cm^-2]\nweight_FSI_MSN = ḡ_FSI_MSN / (N_FSI * density_FSI_MSN) ## normalized synaptic weight\n\ng = MetaDiGraph()\nadd_edge!(g, fsi => msn, weight = weight_FSI_MSN, density = density_FSI_MSN)\nadd_edge!(g, msn => gpe, weight = weight_MSN_GPe, density = density_MSN_GPe)\nadd_edge!(g, gpe => stn, weight = weight_GPe_STN, density = density_GPe_STN)\nadd_edge!(g, stn => fsi, weight = weight_STN_FSI, density = density_STN_FSI)\n\n@named sys = system_from_graph(g)\n\nprob = SDEProblem(sys, [], tspan, [])\nens_prob = EnsembleProblem(prob)\nens_sol = solve(ens_prob, RKMil(), dt = dt, saveat = dt, trajectories = 3);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute and compare power spectra for all neural populations in Parkinsonian condition against their counterparts in baseline conditions.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"powerspectrumplot(fig[2,1], msn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"MSN (PD)\")\n\npowerspectrumplot(fig[2,2], fsi, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"FSI (PD)\")\n\npowerspectrumplot(fig[2,3], gpe, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"GPe (PD)\")\n\npowerspectrumplot(fig[2,4], stn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"STN (PD)\")\n\nresize!(fig.scene, (1600, 900))\nfig","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"We see the emergence of strong beta oscillations in the Parkinsonian condition compared to the baseline condition for all neural populations. This aligns with the findings of Adam et al. and reflects the pathological synchrony observed in Parkinson's disease.","category":"page"},{"location":"tutorials/basal_ganglia/#References","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"References","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Adam, Elie M., et al. \"Deep brain stimulation in the subthalamic nucleus for Parkinson's disease can restore dynamics of striatal networks.\" Proceedings of the National Academy of Sciences 119.19 (2022): e2120808119.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"EditURL = \"neural_assembly.jl\"","category":"page"},{"location":"tutorials/neural_assembly/#Bottom-up-construction-of-a-neural-assembly","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"","category":"section"},{"location":"tutorials/neural_assembly/#Introduction","page":"Bottom-up construction of a neural assembly","title":"Introduction","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"This tutorial goes through the process of building a neural assembly that is part of a larger model that performs category learning of images [1]. We will follow a bottom-up approach with these steps :","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"build a model of a single neuron\nexpand that model by connecting a few neurons into a local circuit\ndefine a \"winner-takes-all\" (WTA) circuit with lateral inhibition\nbuild a cortical block by connecting multiple WTAs together with feed-forward inhibition\nconnect the cortical block to a model of an ascending system\nadd a source of visual input (images) and a cortical block representing visual cortex to our model and simulate visual processing","category":"page"},{"location":"tutorials/neural_assembly/#Single-spiking-neuron-from-Hodgkin-Huxley-model","page":"Bottom-up construction of a neural assembly","title":"Single spiking neuron from Hodgkin-Huxley model","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"(Image: fig1)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Hodgkin-Huxley (HH) formalism to describe membrane potential of a single neuron","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":" beginalign\n C_mfracdVdt = -g_L(V-V_L) - g_Nam^3h(V-V_Na) -g_Kn^4(V-V_K) + I_in - I_syn \n fracdmdt = alpha_m(V)(1-m) + beta_m(V)m \n fracdhdt = alpha_h(V)(1-h) + beta_h(V)h \n fracdndt = alpha_n(V)(1-n) + beta_n(V)n\n endalign","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"using Neuroblox\nusing OrdinaryDiffEq ## to build the ODE problem and solve it, gain access to multiple solvers from this\nusing Random ## for generating random variables\nusing CairoMakie ## for customized plotting recipies for blox\nusing CSV ## to read data from CSV files\nusing DataFrames ## to format the data into DataFrames\nusing Downloads ## to download image stimuli files","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"define a single excitatory neuron 'blox' with steady input current I_bg = 0.5 microA/cm2","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"nn1 = HHNeuronExciBlox(name=Symbol(\"nrn1\"), I_bg=0.5)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"define graph and add the single neuron 'blox' as a single node into the graph","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"g = MetaDiGraph() ## defines a graph\nadd_blox!(g, nn1) ## adds the defined blocks into the graph","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"create an ODESystem from the graph","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"@named sys = system_from_graph(g)\nlength(unknowns(sys)) ## shows the number of variables in the simplified system","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"To solve the system, we first create an Ordinary Differential Equation Problem and then solve it over the tspan of (0,1e) using a Vern7() solver. The solution is saved every 0.1ms. The unit of time in Neuroblox is 1ms.","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"prob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1);\nnothing #hide","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"acessing the voltage timeseries from the neuron block and plotting the voltage","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"v = voltage_timeseries(nn1, sol)\n\nfig = Figure();\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Voltage (mv)\")\ncl = get_neuron_color(nn1) #specify color based on neuron type (excitatory/inhibitory)\nlines!(ax, sol.t, v, color=cl)\nfig ## to display the figure","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Suggestion : Try different values of input current 'I_bg' and run the entire code block to see the output activity","category":"page"},{"location":"tutorials/neural_assembly/#Connecting-three-neurons-through-synapses-to-make-a-local-feed-forward-circuit","page":"Bottom-up construction of a neural assembly","title":"Connecting three neurons through synapses to make a local feed-forward circuit","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"(Image: fig2)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"# While creating a system of multiple components (neurons in this case), each component should be defined within the same namespace. So first\n# we define a global namespace.\nglobal_namespace=:g\n\n# define three neurons, two excitatory and one inhibitory\n\nnn1 = HHNeuronExciBlox(name=Symbol(\"nrn1\"), I_bg=0.4, namespace=global_namespace)\nnn2 = HHNeuronInhibBlox(name=Symbol(\"nrn2\"), I_bg=0.1, namespace=global_namespace)\nnn3 = HHNeuronExciBlox(name=Symbol(\"nrn3\"), I_bg=1.4, namespace=global_namespace)\n\n# defien graph and connect the nodes with the edges (synapses in this case), with the synaptic 'weights' specified as arguments\ng = MetaDiGraph()\nadd_edge!(g, nn1 => nn2, weight = 1) ##connection from neuron 1 to neuron 2 (nn1 to nn2)\nadd_edge!(g, nn2 => nn3, weight = 0.2) ##connection from neuron 2 to neuron 3 (nn2 to nn3)\nadd_edge!(g, nn1 => nn3, weight = 0.5) ##connection from neuron 1 to neuron 3 (nn2 to nn3)\n\n# create an ODESystem from the graph and then solve it using an ODE solver\n@named sys = system_from_graph(g)\nprob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1);\n\n# plotting membrane voltage activity of all neurons in a stacked form\n\nstackplot([nn1,nn2,nn3], sol)\t## stackplot(, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Suggestion : Try different values of input currents 'I_bg' and connection weights. One can try different permutations of excitatory and inhibitory neurons.","category":"page"},{"location":"tutorials/neural_assembly/#Creating-a-lateral-inhibition-circuit-(the-\"winner-takes-all\"-circuit)-in-superficial-cortical-layer","page":"Bottom-up construction of a neural assembly","title":"Creating a lateral inhibition circuit (the \"winner-takes-all\" circuit) in superficial cortical layer","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"(Image: fig3)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\nN_exci = 5; ##number of excitatory neurons\n\nn_inh = HHNeuronInhibBlox(name = Symbol(\"inh\"), namespace=global_namespace, G_syn = 4.0, τ = 70) ##feedback inhibitory interneuron neuron\n\n##creating an array of excitatory pyramidal neurons\nn_excis = [HHNeuronExciBlox(\n name = Symbol(\"exci$i\"),\n namespace=global_namespace,\n G_syn = 3.0,\n τ = 5,\n I_bg = 5*rand(),\n ) for i = 1:N_exci]\n\ng = MetaDiGraph()\n\nfor i in Base.OneTo(N_exci)\n add_edge!(g, n_inh => n_excis[i], weight = 1.0)\n add_edge!(g, n_excis[i] => n_inh, weight = 1.0)\nend\n\n@named sys = system_from_graph(g)\nprob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1)\nstackplot(vcat(n_excis, n_inh), sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Suggestion : Instead of uniform random input current in each excitatory neuron, try different configurations (random or constant) of input currents I_bg for each neuron. One can vary the size of circuit by changing number of excitatory neurons.","category":"page"},{"location":"tutorials/neural_assembly/#Creating-lateral-inhibition-\"winner-take-all\"-circuit-(WTA)-blocks-from-the-inbuilt-functions-and-connecting-two-WTA-circuit-blocks","page":"Bottom-up construction of a neural assembly","title":"Creating lateral inhibition \"winner-take-all\" circuit (WTA) blocks from the inbuilt functions and connecting two WTA circuit blocks","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\nN_exci = 5 ##number of excitatory neurons in each WTA circuit\nwta1 = WinnerTakeAllBlox(name=Symbol(\"wta1\"), I_bg=5.0, N_exci=N_exci, namespace=global_namespace) ##for a single valued input current, each neuron of the WTA circuit will recieve a uniformly distributed random input from 0 to I_bg\nwta2 = WinnerTakeAllBlox(name=Symbol(\"wta2\"), I_bg=4.0, N_exci=N_exci, namespace=global_namespace)\n\ng = MetaDiGraph()\nadd_edge!(g, wta1 => wta2, weight=1, density=0.5) ##density keyword sets the connection probability from each excitatory neuron of source WTA circuit to each excitatory neuron of target WTA circuit\n\nsys = system_from_graph(g, name=global_namespace)\nprob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1)\n\nneuron_set = get_neurons([wta1, wta2]) ## extract neurons from a composite blocks\nstackplot(neuron_set,sol)","category":"page"},{"location":"tutorials/neural_assembly/#Creating-a-single-cortical-superficial-layer-block-by-connecting-multiple-WTA-circuits","page":"Bottom-up construction of a neural assembly","title":"Creating a single cortical superficial layer block by connecting multiple WTA circuits","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"This model is SCORT in [1] and looks like this (Image: fig4)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\nN_wta=10 ## number of WTA circuits\n# parameters\nN_exci=5 ##number of pyramidal neurons in each lateral inhibition (WTA) circuit\nG_syn_exci=3.0 ##maximal synaptic conductance in glutamatergic (excitatory) synapses\nG_syn_inhib=4.0 ## maximal synaptic conductance in GABAergic (inhibitory) synapses from feedback interneurons\nG_syn_ff_inhib=3.5 ## maximal synaptic conductance in GABAergic (inhibitory) synapses from feedforward interneurons\nI_bg=5.0 ##background input\ndensity=0.01 ##connection density between WTA circuits\n\n##creating array of WTA ciruits\nwtas = [WinnerTakeAllBlox(;\n name=Symbol(\"wta$i\"),\n namespace=global_namespace,\n N_exci=N_exci,\n G_syn_exci=G_syn_exci,\n G_syn_inhib=G_syn_inhib,\n I_bg = I_bg\n ) for i = 1:N_wta]\n\n##feed-forward interneurons (get input from other pyramidal cells and from the ascending system, largely controls the rhythm)\nn_ff_inh = HHNeuronInhibBlox(;\n name=Symbol(\"ff_inh\"),\n namespace=global_namespace,\n G_syn=G_syn_ff_inhib\n )\n\ng = MetaDiGraph()\n\n# connecting WTA circuits to each other with given connection density, and feedforward interneuron connects to each WTA circuit\nfor i in 1:N_wta\n for j in 1:N_wta\n if j != i\n add_edge!(g, wtas[i] => wtas[j], weight=1, density=density)\n end\n end\n add_edge!(g, n_ff_inh => wtas[i], weight=1)\nend\n\nsys = system_from_graph(g, name=global_namespace)\nprob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1)\n\nneuron_set = get_neurons(vcat(wtas, n_ff_inh)) ## extract neurons from a composite blocks\nstackplot(neuron_set, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Sugestion : try different connection densities and weights and see how it affects the population activity.","category":"page"},{"location":"tutorials/neural_assembly/#Connecting-the-cortical-superficial-layer-block-to-an-ascending-system-block","page":"Bottom-up construction of a neural assembly","title":"Connecting the cortical superficial layer block to an ascending system block","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Now we will expand on the SCORT block of the previous section by defining a block representing an ascending system (ASC1 in [1]) and then connecting the two blocks together.","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\n\n# define ascending system block using a Next Generation Neural Mass model as described in Byrne et. al. 2020.\n# the parameters are fixed to generate a 16 Hz modulating frequency in the cortical neurons\n@named ASC1 = NextGenerationEIBlox(;namespace=global_namespace, Cₑ=2*26,Cᵢ=1*26, Δₑ=0.5, Δᵢ=0.5, η_0ₑ=10.0, η_0ᵢ=0.0, v_synₑₑ=10.0, v_synₑᵢ=-10.0, v_synᵢₑ=10.0, v_synᵢᵢ=-10.0, alpha_invₑₑ=10.0/26, alpha_invₑᵢ=0.8/26, alpha_invᵢₑ=10.0/26, alpha_invᵢᵢ=0.8/26, kₑₑ=0.0*26, kₑᵢ=0.6*26, kᵢₑ=0.6*26, kᵢᵢ=0*26)\n\n# define the superficial layer cortical block using inbuilt function\n# Number if WTA circuits = N_wta=45; number of pyramidal neurons in each WTA circuit = N_exci = 5;\n@named CB = CorticalBlox(N_wta=10, N_exci=5, density=0.01, weight=1, I_bg_ar=7; namespace=global_namespace)\n\n# define graph and connect ASC1->CB\ng = MetaDiGraph()\nadd_edge!(g, ASC1 => CB, weight=44)\n\n# solve the system for time 0 to 1000 ms\nsys = system_from_graph(g, name=global_namespace)\nprob = ODEProblem(sys, [], (0.0, 1000), []) ## tspan = (0,1000)\nsol = solve(prob, Vern7(), saveat=0.1);\nnothing #hide","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"plot neuron time series","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"neuron_set = get_neurons(CB) ## extract neurons from a composite block like CorticalBlox\nn_neurons = 50 ## set number nof neurons to display in the stackplot\nstackplot(neuron_set[1:n_neurons], sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"plot the meanfield of all cortical block neurons (mean membrane voltage)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"mnv = meanfield_timeseries(CB, sol)\nfig = Figure();\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Meanfield voltage (mv)\")\nlines!(ax, sol.t, mnv)\nfig ## to display the figure","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"plot power spectrum of the meanfield (average over membrane potentials)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"powerspectrumplot(CB, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Notice the peak at 16 Hz, representing beta oscillations. Sugestion : try changing parameters of ASC1 to generate different cortical rhythms. See how the peak shifts in the powerspectrum","category":"page"},{"location":"tutorials/neural_assembly/#Creating-simulation-of-visual-stimulus-response-in-cortical-blocks","page":"Bottom-up construction of a neural assembly","title":"Creating simulation of visual stimulus response in cortical blocks","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"(Image: fig5) create cortical blocks for visual area cortex (VAC), anterior cortex (AC) and ascending system block (ASC1)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\n# cortical blox\n@named VAC = CorticalBlox(N_wta=10, N_exci=5, density=0.01, weight=1,I_bg_ar=0; namespace=global_namespace)\n@named AC = CorticalBlox(N_wta=10, N_exci=5, density=0.01, weight=1,I_bg_ar=0; namespace=global_namespace)\n# ascending system blox, modulating frequency set to 16 Hz\n@named ASC1 = NextGenerationEIBlox(;namespace=global_namespace, Cₑ=2*26,Cᵢ=1*26, Δₑ=0.5, Δᵢ=0.5, η_0ₑ=10.0, η_0ᵢ=0.0, v_synₑₑ=10.0, v_synₑᵢ=-10.0, v_synᵢₑ=10.0, v_synᵢᵢ=-10.0, alpha_invₑₑ=10.0/26, alpha_invₑᵢ=0.8/26, alpha_invᵢₑ=10.0/26, alpha_invᵢᵢ=0.8/26, kₑₑ=0.0*26, kₑᵢ=0.6*26, kᵢₑ=0.6*26, kᵢᵢ=0*26)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"create an image source block which takes image data from a .csv file and gives input to visual cortex","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"image_set = CSV.read(Downloads.download(\"raw.githubusercontent.com/Neuroblox/NeurobloxDocsHost/refs/heads/main/data/image_example.csv\"), DataFrame) ## reading data into DataFrame format\nimage_sample = 2 ## set which image to input (from 1 to 1000)\n\n# define stimulus source blox\n# t_stimulus: how long the stimulus is on (in msec)\n# t_pause : how long th estimulus is off (in msec)\n@named stim = ImageStimulus(image_set[[image_sample], :]; namespace=global_namespace, t_stimulus=1000, t_pause=0);\nnothing #hide","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"plot the image that the visual cortex 'sees'","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"pixels = Array(image_set[image_sample, 1:end-1])## access the desired image sample from respective row\npixels = reshape(pixels, 15, 15)## reshape into 15 X 15 square image matrix\nheatmap(pixels,colormap = :gray1) #input image matrix seen as heatmap","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"assemble the blox into a graph and set connections with their keword arguments like connection weight and connection density","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"g = MetaDiGraph()\n\nadd_edge!(g, stim => VAC, weight=14)\nadd_edge!(g, ASC1 => VAC, weight=44)\nadd_edge!(g, ASC1 => AC, weight=44)\nadd_edge!(g, VAC => AC, weight=3, density=0.08)\n\n# define system and solve\nsys = system_from_graph(g, name=global_namespace)\nprob = ODEProblem(sys, [], (0.0, 1000), []) ## tspan = (0,1000)\nsol = solve(prob, Vern7(), saveat=0.1);\nnothing #hide","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Let us now plot neuron potentials, meanfield activity and powerspectrums for the VAC and AC blox. First we show the stackplot of voltage potentials from the first 10 neurons of VAC","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"VAC_neuron_set = get_neurons(VAC) ## extract neurons from VAC\nn_neurons = 40 ##number of neurons displayed. You can try incresing it.\nstackplot(VAC_neuron_set[1:n_neurons],sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"then we plot the meanfield potential out of all neurons within VAC","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"mnv = meanfield_timeseries(VAC, sol)\n\nfig = Figure();\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Voltage (mv)\")\nlines!(ax, sol.t, mnv)\nfig ## to display the figure","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Here is the powerspectrum from all neurons within VAC","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"powerspectrumplot(VAC, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Moving on to the AC blox, we first plot the voltage potential of its neurons","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"AC_neuron_set = get_neurons(AC) ## extract neurons from VAC\nn_neurons = 40\nstackplot(AC_neuron_set[1:n_neurons], sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"followed by the meanfield activity","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"mnv = meanfield_timeseries(AC, sol)\nfig = Figure();\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Voltage (mv)\")\nlines!(ax, sol.t, mnv)\nfig ## to display the figure","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"and finally the AC powerspectrum","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"powerspectrumplot(AC, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Sugestion : Try changing the image samples and notice the change in the spatial firing patterns in VAC and AC neurons. One can make multiple cortical blocks simillar to AC and connect them in various connection topologies. All of them can directly or indirectly get input from VAC.","category":"page"},{"location":"tutorials/neural_assembly/#References","page":"Bottom-up construction of a neural assembly","title":"References","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"[1] Pathak A., Brincat S., Organtzidis H., Strey H., Senneff S., Antzoulatos E., Mujica-Parodi L., Miller E., Granger R. Biomimetic model of corticostriatal micro-assemblies discovers new neural code., bioRxiv 2023.11.06.565902, 2024","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"This page was generated using Literate.jl.","category":"page"},{"location":"api/#API-Documentation","page":"API","title":"API Documentation","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Modules = [Neuroblox]","category":"page"},{"location":"api/#Neuroblox.BalloonModel","page":"API","title":"Neuroblox.BalloonModel","text":"Arguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nlnκ: logarithmic prefactor to signal decay H[1], set to 0 for standard parameter value.\nlnτ: logarithmic prefactor to transit time H[3], set to 0 for standard parameter value.\nlnϵ: logarithm of ratio of intra- to extra-vascular signal\n\nNB: the prefix ln of the variables u, ν, q as well as the parameters κ, τ denotes their transformation into logarithmic space to enforce their positivity. This transformation is considered in the derivates of the model equations below. \n\nCitations:\n\nStephan K E, Weiskopf N, Drysdale P M, Robinson P A, and Friston K J. Comparing Hemodynamic Models with DCM. NeuroImage 38, no. 3 (2007): 387–401. doi: 10.1016/j.neuroimage.2007.07.040\nHofmann D, Chesebro A G, Rackauckas C, Mujica-Parodi L R, Friston K J, Edelman A, and Strey H H. Leveraging Julia's Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed, 2023. doi: 10.1101/2023.10.27.564407\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.DBS-Tuple{}","page":"API","title":"Neuroblox.DBS","text":"DBS(; name, namespace=nothing, frequency=130.0, amplitude=2.5, pulse_width=0.066, \n offset=0.0, start_time=0.0, smooth=1e-4)\n\nCreate a continuous deep brain stimulation (DBS) stimulus with regular pulses.\n\nArguments:\n\nname: Name given to ODESystem object within the blox\nnamespace: Additional namespace above name if needed for inheritance\nfrequency: Pulse frequency in Hz \namplitude: Pulse amplitude in arbitrary units\npulse_width: Duration of each pulse in ms\noffset: Baseline value of the signal between pulses\nstart_time: Time delay before stimulation begins in ms\nsmooth: Smoothing parameter for pulse transitions, set to 0 for sharp transitions\n\nReturns a DBS stimulus blox that outputs square pulses with specified parameters.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.Generic2dOscillator","page":"API","title":"Neuroblox.Generic2dOscillator","text":"Generic2dOscillator(name, namespace, ...)\n\nThe Generic2dOscillator model is a generic dynamic system with two state\nvariables. The dynamic equations of this model are composed of two ordinary\ndifferential equations comprising two nullclines. The first nullcline is a\ncubic function as it is found in most neuron and population models; the\nsecond nullcline is arbitrarily configurable as a polynomial function up to\nsecond order. The manipulation of the latter nullcline's parameters allows\nto generate a wide range of different behaviours.\n\nEquations:\n\n```math\n \\begin{align}\n \\dot{V} &= d \\, \\tau (-f V^3 + e V^2 + g V + \\alpha W + \\gamma I) \\\\\n \\dot{W} &= \\dfrac{d}{\tau}\\,\\,(c V^2 + b V - \\beta W + a)\n \\end{align}\n```\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nOther parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds.\n\nCitations: FitzHugh, R., Impulses and physiological states in theoretical models of nerve membrane, Biophysical Journal 1: 445, 1961.\n\nNagumo et.al, An Active Pulse Transmission Line Simulating Nerve Axon, Proceedings of the IRE 50: 2061, 1962.\n\nStefanescu, R., Jirsa, V.K. Reduced representations of heterogeneous mixed neural networks with synaptic coupling. Physical Review E, 83, 2011.\n\nJirsa VK, Stefanescu R. Neural population modes capture biologically realistic large-scale network dynamics. Bulletin of Mathematical Biology, 2010.\n\nStefanescu, R., Jirsa, V.K. A low dimensional description of globally coupled heterogeneous neural networks of excitatory and inhibitory neurons. PLoS Computational Biology, 4(11), 2008).\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.HarmonicOscillator","page":"API","title":"Neuroblox.HarmonicOscillator","text":"HarmonicOscillator(name, namespace, ω, ζ, k, h)\n\nCreate a harmonic oscillator blox with the specified parameters.\nThe formal definition of this blox is:\n\nfracdxdt = y-(2*omega*zeta*x)+ k*(2pi)*(atan((sumjcn)h)\nfracdydt = -(omega^2)*x\n\nwhere ``jcn`` is any input to the blox.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nω: Base frequency. Note the default value is scaled to give oscillations in milliseconds to match other blocks.\nζ: Damping ratio.\nk: Gain.\nh: Threshold.\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.JansenRit","page":"API","title":"Neuroblox.JansenRit","text":"JansenRit(name, namespace, τ, H, λ, r, cortical, delayed)\n\nCreate a Jansen Rit blox as described in Liu et al.\nThe formal definition of this blox is:\n\nfracdxdt = y-frac2taux\nfracdydt = -fracxtau^2 + fracHtau frac2lambda1+textexp(-r*sumjcn) - lambda\n\nwhere jcn is any input to the blox.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nτ: Time constant. Defaults to 1 for cortical regions, 14 for subcortical.\nH: See equation for use. Defaults to 0.02 for both cortical and subcortical regions.\nλ: See equation for use. Defaults to 5 for cortical regions, 400 for subcortical.\nr: See equation for use. Defaults to 0.15 for cortical regions, 0.1 for subcortical.\ncortical: Boolean to determine whether to use cortical or subcortical parameters. Specifying any of the parameters above will override this.\ndelayed: Boolean to indicate whether states are delayed\n\nCitations:\n\nLiu C, Zhou C, Wang J, Fietkiewicz C, Loparo KA. The role of coupling connections in a model of the cortico-basal ganglia-thalamocortical neural loop for the generation of beta oscillations. Neural Netw. 2020 Mar;123:381-392. doi: 10.1016/j.neunet.2019.12.021.\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.JansenRitSPM12","page":"API","title":"Neuroblox.JansenRitSPM12","text":"Jansen-Rit model block for canonical micro circuit, analogous to the implementation in SPM12\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.KuramotoOscillator","page":"API","title":"Neuroblox.KuramotoOscillator","text":"KuramotoOscillator(name, namespace, ...)\n\nSimple implementation of the Kuramoto oscillator as described in the original paper [1].\nUseful for general models of synchronization and oscillatory behavior.\nThe general form of the Kuramoto oscillator is given by:\nEquations:\n\n```math\n \\begin{equation}\n \\dot{\\theta_i} = \\omega_i + \\frac{1}{N}\\sum_{j=1}^N{K_{i, j}\\text{sin}(\\theta_j - \\theta_i)}\n \\end{equation}\n```\n\nWhere this describes the connection between regions $i$ and $j$. An alternative form\nwhich includes a noise term for each region is also provided, taking the form:\n\n```math\n \\begin{equation}\n \\dot{\\theta_i} = \\omega_i + \\zeta dW_i \\frac{1}{N}\\sum_{j=1}^N{K_{i, j}\\text{sin}(\\theta_j - \\theta_i)}\n \\end{equation}\n```\n\nwhere $W_i$ is a Wiener process and $\\zeta_i$ is the noise strength.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nOther parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds. Default parameter values are taken from [2].\n\nCitations:\n\nKuramoto, Y. (1975). Self-entrainment of a population of coupled non-linear oscillators. In: Araki, H. (eds) International Symposium on Mathematical Problems in Theoretical Physics. Lecture Notes in Physics, vol 39. Springer, Berlin, Heidelberg. https://doi.org/10.1007/BFb0013365\nSermon JJ, Wiest C, Tan H, Denison T, Duchet B. Evoked resonant neural activity long-term dynamics can be reproduced by a computational model with vesicle depletion. Neurobiol Dis. 2024 Jun 14;199:106565. doi: 10.1016/j.nbd.2024.106565. Epub ahead of print. PMID: 38880431.\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.LarterBreakspear","page":"API","title":"Neuroblox.LarterBreakspear","text":"LarterBreakspear(name, namespace, ...)\n\nCreate a Larter Breakspear blox described in Endo et al. For a full list of the parameters used see the reference.\nIf you need to modify the parameters, see Chesebro et al. and van Nieuwenhuizen et al. for physiological ranges.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nOther parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds.\n\nCitations:\n\nEndo H, Hiroe N, Yamashita O. Evaluation of Resting Spatio-Temporal Dynamics of a Neural Mass Model Using Resting fMRI Connectivity and EEG Microstates. Front Comput Neurosci. 2020 Jan 17;13:91. doi: 10.3389/fncom.2019.00091.\nChesebro AG, Mujica-Parodi LR, Weistuch C. Ion gradient-driven bifurcations of a multi-scale neuronal model. Chaos Solitons Fractals. 2023 Feb;167:113120. doi: 10.1016/j.chaos.2023.113120. \nvan Nieuwenhuizen, H, Chesebro, AG, Polis, C, Clarke, K, Strey, HH, Weistuch, C, Mujica-Parodi, LR. Ketosis regulates K+ ion channels, strengthening brain-wide signaling disrupted by age. Preprint. bioRxiv 2023.05.10.540257; doi: https://doi.org/10.1101/2023.05.10.540257. \n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.OUBlox","page":"API","title":"Neuroblox.OUBlox","text":"Ornstein-Uhlenbeck process Blox\n\nvariables: x(t): value jcn: input parameters: τ: relaxation time \tμ: average value \tσ: random noise (variance of OU process is τ*σ^2/2) returns: an ODE System (but with brownian parameters)\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.PINGNeuronExci","page":"API","title":"Neuroblox.PINGNeuronExci","text":"PINGNeuronExci(name, namespace, C, g_Na, V_Na, g_K, V_K, g_L, V_L, I_ext, τ_R, τ_D)\n\nCreate an excitatory neuron from Borgers et al. (2008).\nThe formal definition of this blox is:\n\nfracdVdt = frac1C(-g_Na*m_infty^3*h*(V - V_Na) - g_K*n^4*(V - V_K) - g_L*(V - V_L) + I_ext + jcn)\nm_infty = fraca_m(V)a_m(V) + b_m(V)\nfracdndt = a_n(V)*(1 - n) - b_n(V)*n\nfracdhdt = a_h(V)*(1 - h) - b_h(V)*h\nfracdsdt = frac12*(1 + tanh(V10))*(frac1 - stau_R - fracstau_D)\n\nwhere jcn is any input to the blox. Note that this is a modified Hodgkin-Huxley formalism with an additional synaptic accumulation term. Synapses are added into the jcn term by connecting the postsynaptic neuron's voltage to the presynaptic neuron's output:\n\njcn = w*s*(V_E - V)\n\nwhere w is the weight of the synapse and V_E is the reversal potential of the excitatory synapse.\n\nInputs:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nC: Membrane capacitance (defaults to 1.0).\ng_Na: Sodium conductance (defaults to 100.0).\nV_Na: Sodium reversal potential (defaults to 50.0).\ng_K: Potassium conductance (defaults to 80.0).\nV_K: Potassium reversal potential (defaults to -100.0).\ng_L: Leak conductance (defaults to 0.1).\nV_L: Leak reversal potential (defaults to -67.0).\nI_ext: External current (defaults to 0.0).\nτ_R: Rise time of synaptic conductance (defaults to 0.2).\nτ_D: Decay time of synaptic conductance (defaults to 2.0).\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.PINGNeuronInhib","page":"API","title":"Neuroblox.PINGNeuronInhib","text":"PINGNeuronInhib(name, namespace, C, g_Na, V_Na, g_K, V_K, g_L, V_L, I_ext, τ_R, τ_D)\n\nCreate an inhibitory neuron from Borgers et al. (2008).\nThe formal definition of this blox is:\n\nfracdVdt = frac1C(-g_Na*m_infty^3*h*(V - V_Na) - g_K*n^4*(V - V_K) - g_L*(V - V_L) + I_ext + jcn)\nm_infty = fraca_m(V)a_m(V) + b_m(V)\nfracdndt = a_n(V)*(1 - n) - b_n(V)*n\nfracdhdt = a_h(V)*(1 - h) - b_h(V)*h\nfracdsdt = frac12*(1 + tanh(V10))*(frac1 - stau_R - fracstau_D)\n\nwhere jcn is any input to the blox. Note that this is a modified Hodgkin-Huxley formalism with an additional synaptic accumulation term. Synapses are added into the jcn term by connecting the postsynaptic neuron's voltage to the presynaptic neuron's output:\n\njcn = w*s*(V_I - V)\n\nwhere w is the weight of the synapse and V_I is the reversal potential of the inhibitory synapse.\n\nInputs:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nC: Membrane capacitance (defaults to 1.0).\ng_Na: Sodium conductance (defaults to 35.0).\nV_Na: Sodium reversal potential (defaults to 55.0).\ng_K: Potassium conductance (defaults to 9.0).\nV_K: Potassium reversal potential (defaults to -90.0).\ng_L: Leak conductance (defaults to 0.1).\nV_L: Leak reversal potential (defaults to -65.0).\nI_ext: External current (defaults to 0.0).\nτ_R: Rise time of synaptic conductance (defaults to 0.5).\nτ_D: Decay time of synaptic conductance (defaults to 10.0).\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.Striatum","page":"API","title":"Neuroblox.Striatum","text":"Subcortical blox\nall subcprtical blox used in cortico-striatal model are defined here\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.WilsonCowan","page":"API","title":"Neuroblox.WilsonCowan","text":"WilsonCowan(name, namespace, τ_E, τ_I, a_E, a_I, c_EE, c_IE, c_EI, c_II, θ_E, θ_I, η)\n\nCreate a standard Wilson Cowan blox.\nThe formal definition of this blox is:\n\nfracdEdt = frac-Etau_E + frac11 + textexp(-a_E*(c_EE*E - c_IE*I - theta_E + eta*(sumjcn))\nfracdIdt = frac-Itau_I + frac11 + exp(-a_I*(c_EI*E - c_II*I - theta_I)\n\nwhere jcn is any input to the blox.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nOthers: See equation for use.\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.WinnerTakeAllBlox","page":"API","title":"Neuroblox.WinnerTakeAllBlox","text":"WinnerTakeAllBlox\n\nCreates a winner-take-all local circuit found in neocortex, typically 5 pyramidal (excitatory) neurons send synapses to a single interneuron (inhibitory) and receive feedback inhibition from that interneuron.\n\n\n\n\n\n","category":"type"},{"location":"api/#LinearAlgebra.eigen-Union{Tuple{Array{ForwardDiff.Dual{T, P, np}, 2}}, Tuple{np}, Tuple{P}, Tuple{T}} where {T, P, np}","page":"API","title":"LinearAlgebra.eigen","text":"function LinearAlgebra.eigen(M::Matrix{Dual{T, P, np}}) where {T, P, np}\n\nDispatch of LinearAlgebra.eigen for dual matrices with complex numbers. Make the eigenvalue decomposition \namenable to automatic differentiation. To do so compute the analytical derivative of eigenvalues\nand eigenvectors. \n\nArguments:\n- `M`: matrix of type Dual of which to compute the eigenvalue decomposition. \n\nReturns:\n- `Eigen(evals, evecs)`: eigenvalue decomposition returned as type LinearAlgebra.Eigen\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.ARVTarget-NTuple{6, Any}","page":"API","title":"Neuroblox.ARVTarget","text":"ARVTarget Time series data is bandpass filtered and then the power spectrum is computed for a given time interval (control bin), returned as the average value of the power spectral density within a certain frequency band ([lb, ub]).\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.CDVTarget-NTuple{5, Any}","page":"API","title":"Neuroblox.CDVTarget","text":"CDVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians. Circular difference is quantified as the angle of circular_location.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.ControlError-NTuple{8, Any}","page":"API","title":"Neuroblox.ControlError","text":"ControlError Returns the control error (deviation of the actual value from the target value).\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.PDVTarget-NTuple{5, Any}","page":"API","title":"Neuroblox.PDVTarget","text":"PDVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians. Phase deviation is quantified as the angle difference between a given set of signals.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.PLVTarget-NTuple{6, Any}","page":"API","title":"Neuroblox.PLVTarget","text":"PLVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.ProtocolDBS-Tuple{}","page":"API","title":"Neuroblox.ProtocolDBS","text":"ProtocolDBS(; name, namespace=nothing, frequency=130.0, amplitude=2.5,\n pulse_width=0.066, offset=0.0, start_time=0.0, smooth=1e-4,\n pulses_per_burst=10, bursts_per_block=12, \n pre_block_time=200.0, inter_burst_time=200.0)\n\nCreate a deep brain stimulation (DBS) stimulus consisting of a block of pulse bursts.\n\nArguments:\n\nname: Name given to ODESystem object within the blox\nnamespace: Additional namespace above name if needed for inheritance\nfrequency: Pulse frequency in Hz\namplitude: Pulse amplitude in arbitrary units \npulse_width: Duration of each pulse in ms\noffset: Baseline value of the signal between pulses\nstart_time: Time delay before stimulation begins in ms\nsmooth: Smoothing parameter for pulse transitions, set to 0 for sharp transitions\npulsesperburst: Number of pulses in each burst\nburstsperblock: Number of bursts in the block\npreblocktime: Time before the block starts in ms\ninterbursttime: Time between bursts in ms\n\nReturns a DBS stimulus blox that outputs a block of pulse bursts.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.addnontunableparams-Tuple{Any, Any}","page":"API","title":"Neuroblox.addnontunableparams","text":"function addnontunableparams(param, model)\n\nFunction adds parameters of a model that were not marked as tunable to a list of tunable parameters\nand respects the MTK ordering of parameters.\n\nArguments:\n- `paramlist`: parameters of an MTK system that were tagged as tunable\n- `sys`: MTK system\n\nReturns:\n- `completeparamlist`: complete parameter list of a system, including those that were not tagged as tunable\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.bandpassfilter-Tuple{}","page":"API","title":"Neuroblox.bandpassfilter","text":"bandpassfilter takes in time series data and bandpass filters it. It has the following inputs: data: time series data lb: minimum cut-off frequency ub: maximum cut-off frequency fs: sampling frequency order: filter order\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.boldsignal-Tuple{}","page":"API","title":"Neuroblox.boldsignal","text":"Arguments:\n\nname: Name given to ODESystem object within the blox.\nlnϵ : logarithm of ratio of intra- to extra-vascular signal\n\nNB: the prefix ln of the variables ν, q as well as the parameters ϵ denotes their transformation into logarithmic space to enforce their positivity.\n\nCitations:\n\nStephan K E, Weiskopf N, Drysdale P M, Robinson P A, and Friston K J. Comparing Hemodynamic Models with DCM. NeuroImage 38, no. 3 (2007): 387–401. doi: 10.1016/j.neuroimage.2007.07.040\nHofmann D, Chesebro A G, Rackauckas C, Mujica-Parodi L R, Friston K J, Edelman A, and Strey H H. Leveraging Julia's Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed, 2023. doi: 10.1101/2023.10.27.564407\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.complexwavelet","page":"API","title":"Neuroblox.complexwavelet","text":"complexwavelet creates a complex morlet wavelet by windowing a complex sine wave with a Gaussian taper. The morlet wavelet is a special case of a bandpass filter in which the frequency response is Gaussian-shaped. Convolution with a complex wavelet is equivalent to performing a Hilbert transform of a bandpass filtered signal.\n\nIt has the following inputs: data: time series data dt : data sampling rate lb : lower bound wavelet frequency (in Hz) ub : upper bound wavelet frequency (in Hz) a : amplitude of the Gaussian taper, default is 1 n : number of wavelet cycles of the Gaussian taper, defines the trade-off between temporal precision and frequency precision larger n gives better frequency precision at the cost of temporal precision default is 6 Hz m : x-axis offset, default is 0 num_wavelets : number of wavelets to create, default is 5\n\nAnd outputs: complex_wavelet : a family of complex morlet wavelets\n\n\n\n\n\n","category":"function"},{"location":"api/#Neuroblox.csd2mar-NTuple{4, Any}","page":"API","title":"Neuroblox.csd2mar","text":"This function converts a cross-spectral density (CSD) into a multivariate auto-regression (MAR) model. It first transforms the CSD into its cross-correlation function (Wiener-Kinchine theorem) and then computes the MAR model coefficients. csd : cross-spectral density matrix of size MxN; M: number of samples, N: number of cross-spectral dimensions (number of variables squared) w : frequencies dt : time step size p : number of time steps of auto-regressive model\n\nThis function returns coeff : array of length p of coefficient matrices of size sqrt(N)xsqrt(N) noise_cov : noise covariance matrix\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.csd_approx-NTuple{4, Any}","page":"API","title":"Neuroblox.csd_approx","text":"This function implements equation 2 of the spectral DCM paper, Friston et al. 2014 \"A DCM for resting state fMRI\".\nNote that nomenclature is taken from SPM12 code and it does not seem to coincide with the spectral DCM paper's nomenclature. \nFor instance, Gu should represent the spectral component due to external input according to the paper. However, in the code this represents\nthe hidden state fluctuations (which are called Gν in the paper).\nGn in the code corresponds to Ge in the paper, i.e. the observation noise. In the code global and local components are defined, no such distinction\nis discussed in the paper. In fact the parameter γ, corresponding to local component is not present in the paper.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.get_dynamic_states-Tuple{Any}","page":"API","title":"Neuroblox.get_dynamic_states","text":"function get_dynamic_states(sys)\n\nFunction extracts states from the system that are dynamic variables, \nget also indices of external inputs (u(t)) and measurements (like bold(t))\nArguments:\n- `sys`: MTK system\n\nReturns:\n- `sts`: states/unknowns of the system that are neither external inputs nor measurements, i.e. these are the dynamic states\n- `idx`: indices of these states\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.get_input_equations-Tuple{Union{Neuroblox.AbstractBlox, Neuroblox.ObserverBlox}}","page":"API","title":"Neuroblox.get_input_equations","text":"Returns the equations for all input variables of a system, \nassuming they have a form like : `sys.input_variable ~ ...`\nso only the input appears on the LHS.\n\nInput equations are namespaced by the inner namespace of blox\nand then they are returned. This way during system `compose` downstream,\nthe higher-level namespaces will be added to them.\n\nIf blox isa AbstractComponent, it is assumed that it contains a `connector` field,\nwhich holds a `Connector` object with all relevant connections \nfrom lower levels and this level.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.idft-Tuple{AbstractArray}","page":"API","title":"Neuroblox.idft","text":"Plain implementation of idft because AD dispatch versions for ifft don't work still!\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.inner_namespaceof-Tuple{Any}","page":"API","title":"Neuroblox.inner_namespaceof","text":"Returns the complete namespace EXCLUDING the outermost (highest) level.\nThis is useful for manually preparing equations (e.g. connections, see Connector),\nthat will later be composed and will automatically get the outermost namespace.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.learningrate-Tuple{Any, Any}","page":"API","title":"Neuroblox.learningrate","text":"This function computes learning rate. It has the following inputs: outcomes: vector of 1's and 0's for behavioral outcomes windows: number of windows to split the outcome data into And the following outputs: rate: the learning rate across each window\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.mar2csd-Tuple{Any, Any, Any}","page":"API","title":"Neuroblox.mar2csd","text":"This function converts multivariate auto-regression (MAR) model parameters to a cross-spectral density (CSD). A : coefficients of MAR model, array of length p, each element contains the regression coefficients for that particular time-lag. Σ : noise covariance matrix of MAR p : number of time lags freqs : frequencies at which to evaluate the CSD sf : sampling frequency\n\nThis function returns: csd : cross-spectral density matrix of size MxN; M: number of samples, N: number of cross-spectral dimensions (number of variables squared)\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.mar_ml-Tuple{Any, Any}","page":"API","title":"Neuroblox.mar_ml","text":"Maximum likelihood estimator of a multivariate, or vector auto-regressive model. y : MxN Data matrix where M is number of samples and N is number of dimensions p : time lag parameter, also called order of MAR model return values mar[\"A\"] : model parameters is a NxNxP tensor, i.e. one NxN parameter matrix for each time bin k ∈ {1,...,p} mar[\"Σ\"] : noise covariance matrix\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.matlab_norm-Tuple{Any, Any}","page":"API","title":"Neuroblox.matlab_norm","text":"function matlab_norm(A, p)\n\nSimple helper function to implement the norm of a matrix that is equivalent to the one given in MATLAB for order=1, 2, Inf. \nThis is needed for the reproduction of the exact same results of SPM12.\n\nArguments:\n- `A`: matrix\n- `p`: order of norm\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.params-Tuple{Connector}","page":"API","title":"Neuroblox.params","text":"Helper to merge delay and weight into a single vector\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.paramscoping-Tuple{}","page":"API","title":"Neuroblox.paramscoping","text":"function paramscoping(;tunable=true, kwargs...)\n\nScope arguments that are already a symbolic model parameter thereby keep the correct namespace \nand make those that are not yet symbolic a symbol.\nKeyword arguments are used, because parameter definition require names, not just values.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.phase_cos_blox-Union{Tuple{F}, Tuple{Any, Any, F}} where F","page":"API","title":"Neuroblox.phase_cos_blox","text":"phasecosblox is creating a cos with angular frequency ω and variable phase phaseinter has the following parameters: ω: angular frequency t: time phaseinter: a function that returns phase as a function of time and returns: the resulting value\n\nUsage: phaseint = phaseinter(0:0.1:50,phasedata) phaseout(t) = phasecosblox(0.1,t,phaseint) which is now a function of time and can be used in an input blox you can also use the dot operator to calculate time-series signal = phaseout.(collect(0:0.01:50))\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.phase_inter-Tuple{Any, Any}","page":"API","title":"Neuroblox.phase_inter","text":"phaseinter is creating a function that interpolates the phase data for any time given phaseinter has the following parameters: phaserange: a range, e.g. 0:0.1:50 which should reflect the time points of the data phasedata: phase at equidistant time points and returns: an function that returns an interpolated phase for t in range\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.phase_sin_blox-Union{Tuple{F}, Tuple{Any, Any, F}} where F","page":"API","title":"Neuroblox.phase_sin_blox","text":"phasesinblox is creating a sin with angular frequency ω and variable phase phaseinter has the following parameters: ω: angular frequency t: time phaseinter: a function that returns phase as a function of time and returns: the resulting value\n\nUsage: phaseint = phaseinter(0:0.1:50,phasedata) phaseout(t) = phasesinblox(0.1,t,phaseint) which is now a function of time and can be used in an input blox you can also use the dot operator to calculate time-series signal = phaseout.(collect(0:0.01:50))\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.phaseangle-Tuple{}","page":"API","title":"Neuroblox.phaseangle","text":"phaseangle takes in time series data, hilbert transforms it, and estimates the phase angle.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.random_initials-Tuple{ODESystem, Any}","page":"API","title":"Neuroblox.random_initials","text":"random_initials creates a vector of random initial conditions for an ODESystem that is composed of a list of blox. The function finds the initial conditions in the blox and then sets a random value in between range tuple given for that state.\n\nIt has the following inputs: odesys: ODESystem blox : list of blox\n\nAnd outputs: u0 : Float64 vector of initial conditions\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.sample_affect!-NTuple{4, Any}","page":"API","title":"Neuroblox.sample_affect!","text":"Non-symbolic, time-block-based way of `@register_symbolic sample_poisson(λ)`.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.setup_sDCM-NTuple{9, Any}","page":"API","title":"Neuroblox.setup_sDCM","text":"function setup_sDCM(data, stateevolutionmodel, initcond, csdsetup, priors, hyperpriors, indices)\n\nInterface function to performs variational inference to fit model parameters to empirical cross spectral density.\nThe current implementation provides a Variational Laplace fit (see function above `variationalbayes`).\n\nArguments:\n- `data` : dataframe with column names corresponding to the regions of measurement.\n- `model` : MTK model, including state evolution and measurement.\n- `initcond` : dictionary of initial conditions, numerical values for all states\n- `csdsetup` : dictionary of parameters required for the computation of the cross spectral density\n-- `dt` : sampling interval\n-- `freq` : frequencies at which to evaluate the CSD\n-- `p` : order parameter of the multivariate autoregression model\n- `priors` : dataframe of parameters with the following columns:\n-- `name` : corresponds to MTK model name\n-- `mean` : corresponds to prior mean value\n-- `variance` : corresponds to the prior variances\n- `hyperpriors` : dataframe of parameters with the following columns:\n-- `Πλ_pr` : prior precision matrix for λ hyperparameter(s)\n-- `μλ_pr` : prior mean(s) for λ hyperparameter(s)\n- `indices` : indices to separate model parameters from other parameters. Needed for the computation of AD gradient.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.spm_logdet-Tuple{Any}","page":"API","title":"Neuroblox.spm_logdet","text":"function spm_logdet(M)\n\nSPM12 style implementation of the logarithm of the determinant of a matrix.\n\nArguments:\n- `M`: matrix\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.system_from_graph","page":"API","title":"Neuroblox.system_from_graph","text":"system_from_graph(g::MetaDiGraph, p=Num[]; name, simplify=true, graphdynamics=false, kwargs...)\n\nTake in a MetaDiGraph g describing a network of neural structures (and optionally a vector of extra parameters p) and construct a System which can be used to construct various Problem types (i.e. ODEProblem) for use with DifferentialEquations.jl solvers.\n\nIf simplify is set to true (the default), then the resulting system will have structural_simplify called on it with any remaining keyword arguments forwared to structural_simplify. That is,\n\n@named sys = system_from_graph(g; kwarg1=x, kwarg2=y)\n\nis equivalent to\n\n@named sys = system_from_graph(g; simplify=false)\nsys = structural_simplify(sys; kwarg1=x, kwarg2=y)\n\nSee the docstring for structural_simplify for information on which options it supports.\n\nIf graphdynamics=true (defaults to false), the output will be a GraphSystem from GraphDynamics.jl, and the kwargs will be sent to the GraphDynamics constructor instead of using ModelingToolkit.jl. The GraphDynamics.jl backend is typically significantly faster for large neural systems than the default backend, but is experimental and does not yet support all Neuroblox.jl features. \n\n\n\n\n\n","category":"function"},{"location":"api/#Neuroblox.vecparam-Tuple{OrderedCollections.OrderedDict}","page":"API","title":"Neuroblox.vecparam","text":"vecparam(param::OrderedDict)\n\nFunction to flatten an ordered dictionary of model parameters and return a simple list of parameter values.\n\nArguments:\n- `param`: dictionary of model parameters (may contain numbers and lists of numbers)\n\n\n\n\n\n","category":"method"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"EditURL = \"spectralDCM.jl\"","category":"page"},{"location":"tutorials/spectralDCM/#Solving-Inverse-Problems-with-Spectral-Dynamic-Causal-Modeling","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"","category":"section"},{"location":"tutorials/spectralDCM/#Introduction","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Introduction","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Neuroblox provides you with a comprehensive environment for simulations as we have explored previously, but its functionality doesn't stop there. We will now pivot and turn our attention to a different kind of problem: inferring model parameters, that is solving inverse problems, from time series. The method of choice is one of the most widely spread in imaging neuroscience, spectral Dynamic Causal Modeling (spDCM)[1,2]. In this tutorial we will introduce how to perform a spDCM analysis on simulated data. To do so we roughly reproduce the procedure in the SPM12 script DEM_demo_induced_fMRI.m in Neuroblox. This work was also presented in Hofmann et al.[2]","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"In this tutorial we will define a circuit of three linear neuronal mass models, all driven by an Ornstein-Uhlenbeck process. We will model fMRI data by a balloon model and BOLD signal on top. After simulation of this simple model we will use spectral Dynamic Causal Modeling to infer some of the model parameters from the simulation time series.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"(Image: Workflow illustration)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"A brief outline of the procedure we will pursue:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"define the graph, add blocks -> section A, B and C in the figure\nsimulate the model -> instead we could also use actual data, section D in figure\ncompute the cross spectral density\nsetup the DCM\nestimate parameters\nplot the results","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"using Neuroblox\nusing LinearAlgebra\nusing StochasticDiffEq\nusing DataFrames\nusing OrderedCollections\nusing CairoMakie\nusing ModelingToolkit","category":"page"},{"location":"tutorials/spectralDCM/#Model-simulation","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Model simulation","text":"","category":"section"},{"location":"tutorials/spectralDCM/#Define-the-model","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Define the model","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We will define a model of 3 regions. This means first of all to define a graph. To this graph we will add three linear neuronal mass models which constitute the (hidden) neuronal dynamics. These constitute three nodes of the graph. Next we will also need some input that stimulates the activity, we use simple Ornstein-Uhlenbeck blocks to create stochastic inputs. One per region. We want to simulate fMRI signals thus we will need to also add a BalloonModel per region. Note that the Ornstein-Uhlenbeck block will feed into the linear neural mass which in turn will feed into the BalloonModel blox. This needs to be represented by the way we define the edges.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"nr = 3 # number of regions\ng = MetaDiGraph()\nregions = []; # list of neural mass blocks to then connect them to each other with an adjacency matrix `A_true`\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Now add the different blocks to each region and connect the blocks within each region:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"for i = 1:nr\n region = LinearNeuralMass(;name=Symbol(\"r$(i)₊lm\"))\n push!(regions, region) # store neural mass model for connection of regions\n\n # add Ornstein-Uhlenbeck block as noisy input to the current region\n input = OUBlox(;name=Symbol(\"r$(i)₊ou\"), σ=0.1)\n add_edge!(g, input => region; :weight => 1/16) # Note that 1/16 is taken from SPM12, this stabilizes the balloon model simulation. Alternatively the noise of the Ornstein-Uhlenbeck block or the weight of the edge connecting neuronal activity and balloon model could be reduced to guarantee numerical stability.\n\n # simulate fMRI signal with BalloonModel which includes the BOLD signal on top of the balloon model dynamics\n measurement = BalloonModel(;name=Symbol(\"r$(i)₊bm\"))\n add_edge!(g, region => measurement; :weight => 1.0)\nend","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Next we define the between-region connectivity matrix and make sure that it is diagonally dominant to guarantee numerical stability (see Gershgorin theorem).","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"A_true = 0.1*randn(nr, nr)\nA_true -= diagm(map(a -> sum(abs, a), eachrow(A_true))) # ensure diagonal dominance of matrix","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Instead of a random matrix use the same matrix as is defined in [3]","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"A_true = [[-0.5 -2 0]; [0.4 -0.5 -0.3]; [0 0.2 -0.5]]\nfor idx in CartesianIndices(A_true)\n add_edge!(g, regions[idx[1]] => regions[idx[2]]; :weight => A_true[idx[1], idx[2]])\nend","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"finally we compose the simulation model","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"@named simmodel = system_from_graph(g, split=false)","category":"page"},{"location":"tutorials/spectralDCM/#Run-the-simulation-and-plot-the-results","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Run the simulation and plot the results","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"setup simulation of the model, time in seconds","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"tspan = (0.0, 612.0)\nprob = SDEProblem(simmodel, [], tspan)\ndt = 2.0 # two seconds as measurement interval for fMRI\nsol = solve(prob, ImplicitRKMil(), saveat=dt);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"plot bold signal time series","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"idx_m = get_idx_tagged_vars(simmodel, \"measurement\") # get index of bold signal\nf = Figure()\nax = Axis(f[1, 1],\n title = \"fMRI time series\",\n xlabel = \"Time [s]\",\n ylabel = \"BOLD\",\n)\nlines!(ax, sol, idxs=idx_m)\nf","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We note that the initial spike is not meaningful and a result of the equilibration of the stochastic process thus we remove it.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"dfsol = DataFrame(sol[ceil(Int, 101/dt):end]);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/#Estimate-and-plot-the-cross-spectral-densities","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Estimate and plot the cross-spectral densities","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"data = Matrix(dfsol[:, idx_m]);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We compute the cross-spectral density by fitting a linear model of order p and then compute the csd analytically from the parameters of the multivariate autoregressive model","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"p = 8\nmar = mar_ml(data, p) # maximum likelihood estimation of the MAR coefficients and noise covariance matrix\nns = size(data, 1)\nfreq = range(min(128, ns*dt)^-1, max(8, 2*dt)^-1, 32)\ncsd = mar2csd(mar, freq, dt^-1);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Now plot the cross-spectrum:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"fig = Figure(size=(1200, 800))\ngrid = fig[1, 1] = GridLayout()\nfor i = 1:nr\n for j = 1:nr\n ax = Axis(grid[i, j])\n lines!(ax, freq, real.(csd[:, i, j]))\n end\nend\nfig","category":"page"},{"location":"tutorials/spectralDCM/#Model-Inference","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Model Inference","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We will now assemble a new model that is used for fitting the previous simulations. This procedure is similar to before with the difference that we will define global parameters and use tags such as [tunable=false/true] to define which parameters we will want to estimate. Note that parameters are tunable by default.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"g = MetaDiGraph()\nregions = []; # list of neural mass blocks to then connect them to each other with an adjacency matrix `A`\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"The following parameters are shared accross regions, which is why we define them here.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"@parameters lnκ=0.0 [tunable=false] lnϵ=0.0 [tunable=false] lnτ=0.0 [tunable=false] # lnκ: decay parameter for hemodynamics; lnϵ: ratio of intra- to extra-vascular components, lnτ: transit time scale\n@parameters C=1/16 [tunable=false] # note that C=1/16 is taken from SPM12 and stabilizes the balloon model simulation. See also comment above.\n\nfor i = 1:nr\n region = LinearNeuralMass(;name=Symbol(\"r$(i)₊lm\"))\n push!(regions, region)\n input = ExternalInput(;name=Symbol(\"r$(i)₊ei\"))\n add_edge!(g, input => region; :weight => C)\n\n # we assume fMRI signal and model them with a BalloonModel\n measurement = BalloonModel(;name=Symbol(\"r$(i)₊bm\"), lnτ=lnτ, lnκ=lnκ, lnϵ=lnϵ)\n add_edge!(g, region => measurement; :weight => 1.0)\nend\n\nA_prior = 0.01*randn(nr, nr)\nA_prior -= diagm(diag(A_prior)) # remove the diagonal","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Since we want to optimize these weights we turn them into symbolic parameters: Add the symbolic weights to the edges and connect regions.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"A = []\nfor (i, a) in enumerate(vec(A_prior))\n symb = Symbol(\"A$(i)\")\n push!(A, only(@parameters $symb = a))\nend","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"With the function untune!` we can list indices of parameters whose tunable flag should be set to false. For instance the first element in the second row:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"untune!(A, [])\nfor (i, idx) in enumerate(CartesianIndices(A_prior))\n if idx[1] == idx[2]\n add_edge!(g, regions[idx[1]] => regions[idx[2]]; :weight => -exp(A[i])/2) # -exp(A[i])/2: treatement of diagonal elements in SPM12 to make diagonal dominance (see Gershgorin Theorem) more likely but it is not guaranteed\n else\n add_edge!(g, regions[idx[2]] => regions[idx[1]]; :weight => A[i])\n end\nend\n\n@named fitmodel = system_from_graph(g, split=false)","category":"page"},{"location":"tutorials/spectralDCM/#Setup-spectral-DCM","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Setup spectral DCM","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"max_iter = 128; # maximum number of iterations\n# attribute initial conditions to states\nsts, _ = get_dynamic_states(fitmodel);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"the following step is needed if the model's Jacobian would give degenerate eigenvalues if expanded around 0 (which is the default expansion)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"perturbedfp = Dict(sts .=> abs.(0.001*rand(length(sts)))) # slight noise to avoid issues with Automatic Differentiation. TODO: find different solution, this is hacky.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We can use the default prior function to use standardized prior values as given in SPM12.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"pmean, pcovariance, indices = defaultprior(fitmodel, nr)\n\npriors = (μθ_pr = pmean,\n Σθ_pr = pcovariance\n );\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Setup hyper parameter prior as well:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"hyperpriors = Dict(:Πλ_pr => 128.0*ones(1, 1), # prior metaparameter precision, needs to be a matrix\n :μλ_pr => [8.0] # prior metaparameter mean, needs to be a vector\n );\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"To compute the cross spectral densities we need to provide the sampling interval of the time series, the frequency axis and the order of the multivariate autoregressive model:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"csdsetup = (mar_order = p, freq = freq, dt = dt);\n\n_, s_bold = get_eqidx_tagged_vars(fitmodel, \"measurement\"); # get bold signal variables\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Prepare the DCM:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"(state, setup) = setup_sDCM(dfsol[:, String.(Symbol.(s_bold))], fitmodel, perturbedfp, csdsetup, priors, hyperpriors, indices, pmean, \"fMRI\");\n\n# HACK: on machines with very small amounts of RAM, Julia can run out of stack space while compiling the code called in this loop\n# this should be rewritten to abuse the compiler less, but for now, an easy solution is just to run it with more allocated stack space.\nwith_stack(f, n) = fetch(schedule(Task(f, n)));\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We are ready to run the optimization procedure! :)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"with_stack(5_000_000) do # 5MB of stack space\n for iter in 1:max_iter\n state.iter = iter\n run_sDCM_iteration!(state, setup)\n print(\"iteration: \", iter, \" - F:\", state.F[end] - state.F[2], \" - dF predicted:\", state.dF[end], \"\\n\")\n if iter >= 4\n criterion = state.dF[end-3:end] .< setup.tolerance\n if all(criterion)\n print(\"convergence\\n\")\n break\n end\n end\n end\nend","category":"page"},{"location":"tutorials/spectralDCM/#Plot-Results","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Plot Results","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Plot the free energy evolution over optimization iterations:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"freeenergy(state)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Plot the estimated posterior of the effective connectivity and compare that to the true parameter values. Bar hight are the posterior mean and error bars are the standard deviation of the posterior.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"ecbarplot(state, setup, A_true)","category":"page"},{"location":"tutorials/spectralDCM/#References","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"References","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"[1] Novelli, Leonardo, Karl Friston, and Adeel Razi. “Spectral Dynamic Causal Modeling: A Didactic Introduction and Its Relationship with Functional Connectivity.” Network Neuroscience 8, no. 1 (April 1, 2024): 178–202. \n[2] Hofmann, David, Anthony G. Chesebro, Chris Rackauckas, Lilianne R. Mujica-Parodi, Karl J. Friston, Alan Edelman, and Helmut H. Strey. “Leveraging Julia’s Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed.” bioRxiv: The Preprint Server for Biology, 2023. \n[3] Friston, Karl J., Joshua Kahan, Bharat Biswal, and Adeel Razi. “A DCM for Resting State fMRI.” NeuroImage 94 (July 2014): 396–407.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"This page was generated using Literate.jl.","category":"page"},{"location":"release_notes/#Release-Notes","page":"Release Notes","title":"Release Notes","text":"","category":"section"},{"location":"release_notes/#v0.3","page":"Release Notes","title":"v0.3","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Initial Release!","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"EditURL = \"getting_started.jl\"","category":"page"},{"location":"getting_started/#Getting-Started","page":"Getting Started","title":"Getting Started","text":"","category":"section"},{"location":"getting_started/#Getting-Started-with-Julia","page":"Getting Started","title":"Getting Started with Julia","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Here we would like to summarize some resources for people that are interested in learning more about the Julia language before or while exploring Neuroblox. Please follow the links below for introductory material on the language that is inclusive to all users; people familiar with programming or not, people with a mathematics, engineering, or science background :","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Introduction to Julia by Matt Bauman at the JuliaCon 2024\nJulia Tutorials & Workshops, a collection of training materials from the official Julia website.\nModern Julia Workflows, an introduction to how to write and share your Julia code effectively with tips & tricks.","category":"page"},{"location":"getting_started/#getting_started_julia","page":"Getting Started","title":"Getting Started with Neuroblox","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"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.","category":"page"},{"location":"getting_started/#The-Wilson-Cowan-Model","page":"Getting Started","title":"The Wilson-Cowan Model","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Each Wilson-Cowan neural mass is described by the following equations:","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"beginalign\nnonumber\nfracdEdt = frac-Etau_E + S_E(c_EEE - c_IEI + etatextstylesumjcn)10pt\nnonumber\nfracdIdt = frac-Itau_I + S_Ileft(c_EIE - c_IIIright)\nendalign","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"where E and I denote the activity levels of the excitatory and inhibitory populations, respectively. The terms fracdEdt and fracdIdt describe the rate of change of these activity levels over time. The parameters tau_E and tau_I are time constants analogous to membrane time constants in single neuron models, determining how quickly the excitatory and inhibitory populations respond to changes. The coefficients c_EE and c_II represent self-interaction (or feedback) within excitatory and inhibitory populations, while c_IE and c_EI represent the cross-interactions between the two populations. The term etasumjcn represents external input to the excitatory population from other brain regions or external stimuli, with eta acting as a scaling factor. While S_E and S_I are sigmoid functions that represent the responses of neuronal populations to input stimuli, defined as:","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"S_k(x) = frac11 + exp(-a_kx - theta_k)","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"where a_k and theta_k determine the steepness and threshold of the response, respectively.","category":"page"},{"location":"getting_started/#Building-the-Circuit","page":"Getting Started","title":"Building the Circuit","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Let's create an oscillating circuit by connecting two Wilson-Cowan neural masses:","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"using Neuroblox\nusing OrdinaryDiffEq\nusing CairoMakie\n\n# Create two Wilson-Cowan blox\n@named WC1 = WilsonCowan()\n@named WC2 = WilsonCowan()\n\n# Create a graph to represent our circuit\ng = MetaDiGraph()\nadd_blox!.(Ref(g), [WC1, WC2])\n\n# Define the connectivity between the neural masses\nadd_edge!(g, WC1 => WC1; weight = -1) ## recurrent connection from WC1 to itself\nadd_edge!(g, WC1 => WC2; weight = 7) ## connection from WC1 to WC2\nadd_edge!(g, WC2 => WC1; weight = 4) ## connection from WC2 to WC1\nadd_edge!(g, WC2 => WC2; weight = -1) ## recurrent connection from WC2 to itself","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"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.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"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.","category":"page"},{"location":"getting_started/#Creating-the-Model","page":"Getting Started","title":"Creating the Model","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Now, let's build the complete model:","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"@named sys = system_from_graph(g)","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"This creates a differential equations system from our graph representation using ModelingToolkit and symbolically simplifies it for efficient computation.","category":"page"},{"location":"getting_started/#Simulating-the-Model","page":"Getting Started","title":"Simulating the Model","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"We are now ready to simulate our model. The following code creates and solves an ODEProblem for our system, simulating 100 time units of activity. In Neuroblox, the default time unit is milliseconds. We use Rodas4, a solver efficient for stiff problems. The solution is saved every 0.1 ms, allowing us to observe the detailed evolution of the system's behavior.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"prob = ODEProblem(sys, [], (0.0, 100), [])\nsol = solve(prob, Rodas4(), saveat=0.1)","category":"page"},{"location":"getting_started/#Plotting-simulation-results","page":"Getting Started","title":"Plotting simulation results","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"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.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"E1 = state_timeseries(WC1, sol, \"E\")\nI1 = state_timeseries(WC1, sol, \"I\")\n\nfig = Figure()\nax = Axis(fig[1,1]; xlabel = \"time (ms)\")\nlines!(ax, sol.t, E1, label = \"E\")\nlines!(ax, sol.t, I1, label = \"I\")\nLegend(fig[1,2], ax)\nfig","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"[1] Wilson, H. R., & Cowan, J. D. (1972). Excitatory and inhibitory interactions in localized populations of model neurons. Biophysical journal, 12(1), 1-24.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"[2] Destexhe, A., & Sejnowski, T. J. (2009). The Wilson–Cowan model, 36 years later. Biological cybernetics, 101(1), 1-2.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"This page was generated using Literate.jl.","category":"page"},{"location":"#Neuroblox","page":"Neuroblox","title":"Neuroblox","text":"","category":"section"},{"location":"#About","page":"Neuroblox","title":"About","text":"","category":"section"},{"location":"","page":"Neuroblox","title":"Neuroblox","text":"Neuroblox.jl is designed for computational neuroscience and psychiatry applications. Our tools range from control circuit system identification to brain circuit simulations bridging scales from spiking neurons to fMRI-derived circuits, parameter-fitting models to neuroimaging data, interactions between the brain and other physiological systems, experimental optimization, and scientific machine learning.","category":"page"},{"location":"#Description","page":"Neuroblox","title":"Description","text":"","category":"section"},{"location":"","page":"Neuroblox","title":"Neuroblox","text":"Neuroblox.jl is based on a library of modular computational building blocks (“blox”) in the form of systems of symbolic dynamic differential equations that can be combined to describe large-scale brain dynamics. Once a model is built, it can be simulated efficiently and fit electrophysiological and neuroimaging data. Moreover, the circuit behavior of multiple model variants can be investigated to aid in distinguishing between competing hypotheses. We employ ModelingToolkit.jl to describe the dynamical behavior of blox as symbolic (stochastic/delay) differential equations. Our libraries of modular blox consist of individual neurons (Hodgkin-Huxley, IF, QIF, LIF, etc.), neural mass models (Jansen-Rit, Wilson-Cowan, Lauter-Breakspear, Next Generation, microcanonical circuits etc.) and biomimetically-constrained control circuit elements. A GUI designed to be intuitive to neuroscientists allows researchers to build models that automatically generate high-performance systems of numerical ordinary/stochastic differential equations from which one can run stimulations with parameters fit to experimental data. Our benchmarks show that the increase in speed for simulation often exceeds a factor of 100 as compared to neural mass model implementation by the Virtual Brain (python) and similar packages in MATLAB. For parameter fitting of brain circuit dynamical models, we use Turing.jl to perform probabilistic modeling, including Hamilton-Monte-Carlo sampling and Automated Differentiation Variational Inference.","category":"page"},{"location":"#Licensing","page":"Neuroblox","title":"Licensing","text":"","category":"section"},{"location":"","page":"Neuroblox","title":"Neuroblox","text":"Neuroblox is free for non-commerical and academic use. For full details of the license, please see the Neuroblox EULA. For commercial use, get in contact with sales@neuroblox.org.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"EditURL = \"resting_state.jl\"","category":"page"},{"location":"tutorials/resting_state/#Resting-state-simulation-using-neural-mass-models","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"","category":"section"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"This tutorial will introduce you to simulating resting state brain dynamics using Neuroblox. We will be using the FitzHugh-Nagumo model as a building block. The FitzHugh-Nagumo model is described by the follwoing equations:","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":" beginalign\n dotV = d tau (-f V^3 + e V^2 + alpha W - gamma I_c + sigma w(t) ) \n dotW = dfracdtau(b V - beta W + a + sigma w(t) )\n endalign","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"We start by building the resting state circuit from individual Generic2dOscillator Blox","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"using Neuroblox\nusing CSV\nusing DataFrames\nusing StochasticDiffEq\nusing Random\nusing CairoMakie\nusing Statistics\nusing HypothesisTests\nusing Downloads\nusing SparseArrays\n\nRandom.seed!(123); # Set a fixed RNG seed for reproducible results\n\n# download and read connection matrix from a file\nweights = CSV.read(Downloads.download(\"raw.githubusercontent.com/Neuroblox/NeurobloxDocsHost/refs/heads/main/data/weights.csv\"), DataFrame)\nregion_names = names(weights)\n\nwm = Matrix(weights) ## transform the weights into a matrix\nN_bloxs = size(wm)[1]; ## number of blox components\nnothing #hide","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"You can visualize the sparsity structure by converting the weights matrix into a sparse matrix","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"wm_sparse = SparseMatrixCSC(wm)","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"After the connectivity structure, it's time to define the neural mass components of our model and then use the weight matrix to connect them together into our final system.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"# create an array of neural mass models\nblox = [Generic2dOscillator(name=Symbol(region_names[i]),bn=sqrt(5e-4)) for i in 1:N_bloxs]\n\n# add neural mass models to Graph and connect using the connection matrix\ng = MetaDiGraph()\nadd_blox!.(Ref(g), blox)\ncreate_adjacency_edges!(g, wm)\n\n@named sys = system_from_graph(g);\nnothing #hide","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"To solve the system, we first create an Stochastic Differential Equation Problem and then solve it using a EulerHeun solver. The solution is saved every 0.5 ms. The unit of time in Neuroblox is 1 ms.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"tspan = (0.0, 6e5)\nprob = SDEProblem(sys,rand(-2:0.1:4,76*2), tspan, [])\nsol = solve(prob, EulerHeun(), dt=0.5, saveat=5);\nnothing #hide","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"Let us now plot the voltage potential of the first couple of components. We can extract the voltage timeseries of a blox from the solution object using the voltage_timeseries function.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"v1 = voltage_timeseries(blox[1], sol)\nv2 = voltage_timeseries(blox[2], sol)\n\nfig = Figure()\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Potential\")\nlines!(ax, sol.t, v1)\nlines!(ax, sol.t, v2)\nxlims!(ax, (0, 1000)) ## limit the x-axis to the first second of simulation\nfig","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"To evaluate the connectivity of our simulated resting state network, we calculate the statistically significant correlations","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"step_sz = 1000\ntime_steps = range(1, length(sol.t); step = step_sz)\n\ncs = Array{Float64}(undef, N_bloxs, N_bloxs, length(time_steps)-1)\n\n# First we get the voltage timeseries of all blox in time bins\nfor (i, t) in enumerate(time_steps[1:end-1])\n # Get the voltage timeseries of all blox within a time window in a matrix\n V = voltage_timeseries(blox, sol; ts = t:(t + step_sz))\n # Calculate the correlation matrix of all timeseries per time window\n cs[:,:,i] = cor(V)\nend\n\np = zeros(N_bloxs, N_bloxs)\nfor i in 1:N_bloxs\n for j in 1:N_bloxs\n # Perform a t-test on the voltage correlations across all time windows\n p[i,j] = pvalue(OneSampleTTest(cs[i,j,:]))\n end\nend\n\nfig = Figure()\nax = [\n Axis(fig[1,1]; title = \"Correlation p-values\", aspect = AxisAspect(1)),\n Axis(fig[1,2]; title = \"Adjacency matrix\", aspect = AxisAspect(1))\n]\n# Use logical indexing to only plot the statistically significan p-values (p .< 0.05)\nheatmap!(ax[1], log10.(p) .* (p .< 0.05))\n\nheatmap!(ax[2], wm)\nfig","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"Notice how the correlation heatmap qualitatively matches the sparsity structure that we printed above with wm_sparse.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"This page was generated using Literate.jl.","category":"page"}] +[{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"EditURL = \"parkinsons.jl\"","category":"page"},{"location":"tutorials/parkinsons/#Building-a-model-of-the-Basal-Ganglia-using-Neural-Mass-models","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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]. 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. In this tutorial, we will specifically cover:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"How to create a Jansen-Rit neural mass model in Neuroblox\nHow to create blocks with different default sets of parameters\nUsing symbolic hyperparameters during graph definition\nExtracting specific solutions states for plotting time series data\nRemaking an ODEProblem without needing to recompile an entire ODESystem\nPlotting basic spectrograms of simulations","category":"page"},{"location":"tutorials/parkinsons/#The-Jansen-Rit-Neural-Mass-Model","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"The Jansen-Rit Neural Mass Model","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"The Jansen-Rit model [2] is another popular neural mass model that, like the Wilson-Cowan model from the Getting Started, describes the average activity of neural populations. This resource is a good general introduction to the Jansen-Rit model if you'd like to read more. Each Jansen-Rit unit is defined by the following differential equations:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"beginalign*\nfracdxdt = y-frac2taux 10pt\nfracdydt = -fracxtau^2 + fracHtau left2lambda S(textstylesumjcn) - lambdaright\nendalign*","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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 sumjcn 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:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"S(x) = frac11 + textexp(-rx)","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"where r controls the steepness of the sigmoid, affecting the population's sensitivity to input.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"This model consists of four main components:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"A cortical column consisting of pyramidal cells PY, an excitatory interneuron population EI, and an inhibitory interneuron population II\nA striatal network consisting of two populations of medium spiny neurons, D1 and D2 (named for the different dopamine receptors), and fast-spiking interneurons FSI\nSubcortical structures including the subthalamic nucleus STN, the external segment of the globus pallidus GPE, and the internal segment of the globus pallidus GPI\nA thalamic oscillator Th","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"The connections between these oscillators are shown in the Neuroblox GUI below (blue = excitatory, red = inhibitory connections): (Image: Cortico-basal ganglia-thalamic-cortical loop structure)","category":"page"},{"location":"tutorials/parkinsons/#Setting-Up-the-Model","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Setting Up the Model","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"We're now going to set up this model using code. Let's start by importing the necessary libraries and defining our neural masses:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"using Neuroblox\nusing OrdinaryDiffEq\nusing CairoMakie","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"τ_factor = 1000; ## Convert time units from seconds to milliseconds\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Now we'll setup the neural masses. The values are all taken from the original paper [1], Table 1. 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). 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.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"# Create the cortical oscillators\n@named PY = JansenRit(cortical=true) ## default parameters cortical Jansen Rit blox\n@named EI = JansenRit(τ=0.01*τ_factor, H=20/τ_factor, λ=5, r=5)\n@named II = JansenRit(τ=2.0*τ_factor, H=60/τ_factor, λ=5, r=5)\n\n# Create the striatal oscillators\n@named D1 = JansenRit(τ=0.0022*τ_factor, H=20/τ_factor, λ=300, r=0.3)\n@named D2 = JansenRit(τ=0.0022*τ_factor, H=20/τ_factor, λ=300, r=0.3)\n@named FSI = JansenRit(τ=0.0022*τ_factor, H=20/τ_factor, λ=300, r=0.3)\n\n# Create the remaining subcortical oscillators\n@named STN = JansenRit(τ=0.01*τ_factor, H=20/τ_factor, λ=500, r=0.1)\n@named GPE = JansenRit(cortical=false) ## default parameters subcortical Jansen Rit blox\n@named GPI = JansenRit(cortical=false) ## default parameters subcortical Jansen Rit blox\n@named Th = JansenRit(τ=0.002*τ_factor, H=10/τ_factor, λ=20, r=5);\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/#Building-the-Circuit","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building the Circuit","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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. 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. 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. 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 in Julia, which you should look at if you want to build more general models.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"g = MetaDiGraph() ## define an empty graph\n\nparams = @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\n\n# Create connections\n\n# thalamocortical connection\nadd_edge!(g, Th => EI; weight = C_BGTh➡Cor)\n\n# remaining cortical → subcortical connections\nadd_edge!(g, PY => STN; weight = C_Cor➡BGTh)\nadd_edge!(g, PY => D1; weight = C_BGTh➡Cor)\nadd_edge!(g, PY => D2; weight = C_BGTh➡Cor)\nadd_edge!(g, PY => FSI; weight = C_BGTh➡Cor)\n\n# basal ganglia ↔ thalamus connections\nadd_edge!(g, STN => GPE; weight = C_BGTh)\nadd_edge!(g, STN => GPI; weight = C_BGTh)\nadd_edge!(g, GPE => STN; weight = -0.5*C_BGTh)\nadd_edge!(g, GPE => GPE; weight = -0.5*C_BGTh)\nadd_edge!(g, GPE => GPI; weight = -0.5*C_BGTh)\nadd_edge!(g, GPE => FSI; weight = -0.5*C_BGTh)\nadd_edge!(g, FSI => D1; weight = -0.5*C_BGTh)\nadd_edge!(g, FSI => D2; weight = -0.5*C_BGTh)\nadd_edge!(g, FSI => FSI; weight = -0.5*C_BGTh)\nadd_edge!(g, D1 => D1; weight = -0.5*C_BGTh)\nadd_edge!(g, D1 => D2; weight = -0.5*C_BGTh)\nadd_edge!(g, D1 => GPI; weight = -0.5*C_BGTh)\nadd_edge!(g, D2 => D2; weight = -0.5*C_BGTh)\nadd_edge!(g, D2 => D1; weight = -0.5*C_BGTh)\nadd_edge!(g, D2 => GPE; weight = -0.5*C_BGTh)\nadd_edge!(g, GPI => Th; weight = -0.5*C_BGTh)\n\n# corticocortical connections\nadd_edge!(g, PY => EI; weight = 6*C_Cor)\nadd_edge!(g, PY => II; weight = 1.5*C_Cor)\nadd_edge!(g, EI => PY; weight = 4.8*C_Cor)\nadd_edge!(g, II => PY; weight = -1.5*C_Cor)\nadd_edge!(g, II => II; weight = -3.3*C_Cor);\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/#Creating-the-Model","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Creating the Model","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Let's build the complete model:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"@named final_system = system_from_graph(g);\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"This creates a differential equations system from our graph representation using ModelingToolkit and symbolically simplifies it for efficient computation.","category":"page"},{"location":"tutorials/parkinsons/#Simulating-the-Model","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Simulating the Model","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Lastly, we create the ODEProblemfor our system, select an algorithm, in this caseTsit5()` (see discussion in the previous tutorial about solver choices if you're interested), and simulate 1 second of brain activity.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"sim_dur = 1000.0 ## Simulate for 1 second\nprob = ODEProblem(final_system, [], (0.0, sim_dur)) ## Create the problem to solve\nsol = solve(prob, Tsit5(), saveat=0.1); ## Solve the problem and save every 0.1ms\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/#Visualizing-the-results","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Visualizing the results","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Let's interrogate the solution to see what we have. For the purposes of this tutorial, we'll focus on the striatal oscillations. In this simple model, we should see relatively sharp on/off transitions in the striatal populations. To test this, let's use Symbolic Indexing to access the states we're interested in: the y state of the D1 neuron population.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"idx_func = ModelingToolkit.getu(sol, D1.system.y); ## gets the state index of the D1 neuron population in the solution object\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"Now use this indexing function to plot the solution in a Makie plot (read more about Makie in the docs).","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"fig = Figure() ## create the figure\nax = Axis(fig[1, 1], xlabel=\"Time (ms)\", ylabel=\"y voltage (arbitrary units)\", title=\"D1 population membrane voltage\") ## define one axis for the figure\nlines!(ax, sol.t, idx_func(sol), color = :blue) ## plot the time series of the D1 neuron population\ndisplay(fig) ## display the figure","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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. To do this, we'll use the remake function to create a new ODEProblem with a longer simulation time without rebuilding the entire system.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"sim_dur = 10000.0 ## Simulate for 10 seconds\nprob = remake(prob, tspan = (0.0, sim_dur)) ## Remake the ODEProblem with the new simulation time\nsol = solve(prob, Tsit5(), saveat=0.1); ## Solve the new problem\nnothing #hide","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"powerspectrumplot(D1, sol, state = \"y\", ## specify the block to plot, the solution object, and the state of the block to plot\n method = welch_pgram, window = hanning, ## power spectrum estimation method\n title=\"D1 Population Power Spectrum\")","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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.","category":"page"},{"location":"tutorials/parkinsons/#Further-Exploration","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Further Exploration","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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 C_Cor=60, C_BGTh=60, C_CorBGTh=5, and C_BGThCor=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. 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! If you're inspired to continue experimenting, here are a couple other ideas:","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"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.\nTry changing the lengths of the simulation and the sampling frequency. How does this affect the power spectrum?","category":"page"},{"location":"tutorials/parkinsons/#References","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"References","text":"","category":"section"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"[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 [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.","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"","category":"page"},{"location":"tutorials/parkinsons/","page":"Building a model of the Basal Ganglia using Neural Mass models","title":"Building a model of the Basal Ganglia using Neural Mass models","text":"This page was generated using Literate.jl.","category":"page"},{"location":"install/#Installing-Neuroblox","page":"How to install","title":"Installing Neuroblox","text":"","category":"section"},{"location":"install/","page":"How to install","title":"How to install","text":"To install Neuroblox.jl, you need to first add the JuliaHubRegistry your list of registries that julia checks for available package ","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"using Pkg\nPkg.add(\"PkgAuthentication\")\nusing PkgAuthentication\nPkgAuthentication.install(\"juliahub.com\")\nPkg.Registry.add()","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"The next step is to install Neuroblox from the JuliaHubRegistry. It is also useful to install some other packages that are commonly used with Neuroblox. These packages are used in the tutorials of the next section. We have included Neuroblox and these other packages into a single Project.toml file which you can download and then use it to activate a new environment where all the necessary packages will be installed. To do this first choose a folder where you want this environment to be generated in and then run ","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"using Downloads\n\nDownloads.download(\"raw.githubusercontent.com/Neuroblox/NeurobloxDocsHost/refs/heads/main/Project.toml\", joinpath(@__DIR__, \"Project.toml\"))\nPkg.activate(@__DIR__)\nPkg.instantiate()","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"Please note that after running these commands Neuroblox will also be installed along with all other packages that are used in the tutorials.","category":"page"},{"location":"install/","page":"How to install","title":"How to install","text":"NOTE: If you want to install only Neuroblox and not the other packages used in the tutorials you can run Pkg.add(\"Neuroblox\")","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"EditURL = \"ping_network.jl\"","category":"page"},{"location":"tutorials/ping_network/#Pyramidal-Interneuron-Gamma-network","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"","category":"section"},{"location":"tutorials/ping_network/#Introduction","page":"Pyramidal-Interneuron Gamma network","title":"Introduction","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"This tutorial provides a simple example of how to use the Neuroblox package to simulate a pyramidal-interneuron gamma (PING) network. These networks are generally useful in modeling cortical oscillations and are used in a variety of contexts. This particular example is based on Börgers, Epstein, and Kopell [1] and is a simple example of how to replicate their initial network in Neuroblox. This tutorial will cover the following concepts:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Creating populations of neurons for rapid simulation\nCreating input current sources drawn from user-specified distributions\nCreating a directed graph of connections between neurons\nCompiling the graph into a system of ODEs and simulating the network\nVisualizing the results with raster plots","category":"page"},{"location":"tutorials/ping_network/#Conceptual-definition","page":"Pyramidal-Interneuron Gamma network","title":"Conceptual definition","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"The PING network is a simple model of a cortical network that consists of two populations of neurons: excitatory and inhibitory. We omit the detailed equations of the neurons here, but note they are Hodgkin-Huxley-like equations with a few modifications. Excitatory neurons are reduced Traub-Miles cells [2] and inhibitory neurons are Wang-Buzasaki cells [3]. Both follow Hodgkin-Huxley formalism, i.e., the membrane voltage is governed by the sum of the currents through the sodium, potassium, and leak channels, along with external drive, such that:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"beginequation*\nC fracdVdt = g_textNa m^3 h (V_textNa - V) + g_textK n^4 (V_textK - V) + g_textL (V_textL - V) + I_textext\nendequation*","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"For full details of the model, see Eq. 12-14 on p. 7 of the SI Appendix of Börgers et al. [1]. Here is a visual representation of the network structure and which neurons receive the driving input: (Image: PING network structure)","category":"page"},{"location":"tutorials/ping_network/#Model-setup","page":"Pyramidal-Interneuron Gamma network","title":"Model setup","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"This section sets up the model parameters and the network structure. The network consists of 200 neurons: 40 driven excitatory neurons, 120 other excitatory neurons, and 40 inhibitory neurons. The network is set up as a directed graph with excitatory neurons driving inhibitory neurons and vice versa, with self-inhibition but not self-excitation present.","category":"page"},{"location":"tutorials/ping_network/#Import-the-necessary-packages","page":"Pyramidal-Interneuron Gamma network","title":"Import the necessary packages","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Reasons for each non-Neuroblox package are given in the comments after each.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"using Neuroblox\nusing OrdinaryDiffEq ## to build the ODE problem and solve it, gain access to multiple solvers from this\nusing Distributions ## for statistical distributions\nusing Random ## for random number generation\nusing CairoMakie ## for plotting","category":"page"},{"location":"tutorials/ping_network/#Initialization","page":"Pyramidal-Interneuron Gamma network","title":"Initialization","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Set the random seed to reproduce the plots as shown here exactly. If you want to probe how random variability changes the network, simply omit this line.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Random.seed!(42);\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Setup the hyperparameters for the PING network simulation. The comments note where these parameters are taken from in the Börgers et al. paper [1] or if they were manually tuned for this particular simulation.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"μ_E = 0.8 ## mean of the excitatory neurons' external current, manually tuned from the value on p. 8 of the Appendix\nσ_E = 0.15 ## standard deviation of the excitatory neurons' external current, given on p. 8 of the Appendix\nμ_I = 0.8 ## mean of the inhibitory neurons' external current, given on p. 9 of the Appendix\nσ_I = 0.08 ## standard deviation of the inhibitory neurons' external current, given on p. 9 of the Appendix\n\nNE_driven = 40 ## number of driven excitatory neurons, given on p. 8 of the Appendix. Note all receive constant rather than half stochastic drives.\nNE_other = 120 ## number of non-driven excitatory neurons, given in the Methods section\nNI_driven = 40 ## number of inhibitory neurons (all driven), given in the Methods section\nN_total = NE_driven + NE_other + NI_driven ## total number of neurons in the network\n\nN = N_total ## convenience redefinition to improve the readability of the connection weights\ng_II = 0.2 ## inhibitory-inhibitory connection weight, given on p. 8 of the Appendix\ng_IE = 0.6 ## inhibitory-excitatory connection weight, given on p. 8 of the Appendix\ng_EI = 0.8; ## excitatory-inhibitory connection weight, manually tuned from values given on p. 8 of the Appendix\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Finally, setup the driving currents. All neurons receive a base external current, and the inhibitory and driven excitatory populations receive a second external stimulus current. The undriven excitatory neurons receive a small addition to the base current in lieu of the stochastic current in the original implementation. There is also an external inhibitory bath for the inhibitory neurons - for the importance of this bath see the SI Appendix of Börgers et al. [1]. These currents are specified as distributions using the syntax from Distributions.jl. The advantage to this is that a distribution can be given to a call of rand() and the random number will be drawn from the specified distribution. We'll use this call during the neuron creation step below.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"I_base = Normal(0, 0.1) ## base external current for all neurons\nI_driveE = Normal(μ_E, σ_E) ## External current for driven excitatory neurons\nI_driveI = Normal(μ_I, σ_I) ## External current for driven inhibitory neurons\nI_undriven = Normal(0, 0.4) ## Additional noise current for undriven excitatory neurons. Manually tuned.\nI_bath = -0.7; ## External inhibitory bath for inhibitory neurons - value from p. 11 of the SI Appendix\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/#Creating-a-network-in-Neuroblox","page":"Pyramidal-Interneuron Gamma network","title":"Creating a network in Neuroblox","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Creating and running a network of neurons in Neuroblox consists of three steps: defining the neurons, defining the graph of connections between the neurons, and simulating the system represented by the graph.","category":"page"},{"location":"tutorials/ping_network/#Define-the-neurons","page":"Pyramidal-Interneuron Gamma network","title":"Define the neurons","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"The neurons from Börgers et al. [1] are implemented in Neuroblox as PINGNeuronExci and PINGNeuronInhib. We can specify their initial current drives and create the neurons as follows:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"exci_driven = [PINGNeuronExci(name=Symbol(\"ED$i\"), I_ext=rand(I_driveE) + rand(I_base)) for i in 1:NE_driven] ## In-line loop to create the driven excitatory neurons, named ED1, ED2, etc.\nexci_other = [PINGNeuronExci(name=Symbol(\"EO$i\"), I_ext=rand(I_base) + rand(I_undriven)) for i in 1:NE_other] ## In-line loop to create the undriven excitatory neurons, named EO1, EO2, etc.\nexci = [exci_driven; exci_other] ## Concatenate the driven and undriven excitatory neurons into a single vector for convenience\ninhib = [PINGNeuronInhib(name=Symbol(\"ID$i\"), I_ext=rand(I_driveI) + rand(I_base) + I_bath) for i in 1:NI_driven]; ## In-line loop to create the inhibitory neurons, named ID1, ID2, etc.\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/#Define-the-graph-of-network-connections","page":"Pyramidal-Interneuron Gamma network","title":"Define the graph of network connections","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"This portion illustrates how we go about creating a network of neuronal connections.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"g = MetaDiGraph() ## Initialize the graph\n\n# Add the E -> I and I -> E connections\nfor ne ∈ exci\n for ni ∈ inhib\n add_edge!(g, ne => ni; weight=g_EI/N)\n add_edge!(g, ni => ne; weight=g_IE/N)\n end\nend\n\n# Add the I -> I connections\nfor ni1 ∈ inhib\n for ni2 ∈ inhib\n add_edge!(g, ni1 => ni2; weight=g_II/N);\n end\nend","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"note: Alternative graph creation\nIf you are creating a very large network of neurons, it may be more efficient to add all of the nodes first and then all of the edges via an adjacency matrix. To illustrate this, here is an alternative to the previous block that will initialize the same graph. Just uncomment this code and use it to replace the code from the section above. julia g = MetaDiGraph() ## Initialize the graph add_blox!.(Ref(g), [exci; inhib]) ## Add all the neurons to the graph adj = zeros(N_total, N_total) ## Initialize the adjacency matrix for i ∈ 1:NE_driven + NE_other for j ∈ 1:NI_driven adj[i, NE_driven + NE_other + j] = g_EI/N adj[NE_driven + NE_other + j, i] = g_IE/N end end for i ∈ 1:NI_driven for j ∈ 1:NI_driven adj[NE_driven + NE_other + i, NE_driven + NE_other + j] = g_II/N end end create_adjacency_edges!(g, adj)","category":"page"},{"location":"tutorials/ping_network/#Simulate-the-network","page":"Pyramidal-Interneuron Gamma network","title":"Simulate the network","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Now that we have the neurons and the graph, we can simulate the network. We use the system_from_graph function to create a system of ODEs from the graph and then solve it using the DifferentialEquations.jl package, but for performance scaling reasons we will use the experimental option graphdynamics=true which uses a separate compilation backend called GraphDynamics.jl. The GraphDynamics.jl backend is still experimental, and may not yet support all of the standard Neuroblox features, such as those seen in the Spectral DCM tutorial. We choose to solve this system using the Tsit5() solver. If you're coming from Matlab, this is a more efficient solver analogous to ode45. It's a good first try for systems that aren't really stiff. If you want to try other solvers, we'd recommend trying with Vern7() (higher precision but still efficient). If you're really interested in solver choices, one of the great things about Julia is the wide variety of solvers available.","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"tspan = (0.0, 300.0) ## Time span for the simulation - run for 300ms to match the Börgers et al. [1] Figure 1.\n@named sys = system_from_graph(g, graphdynamics=true) ## Use GraphDynamics.jl otherwise this can be a very slow simulation\nprob = ODEProblem(sys, [], tspan) ## Create the problem to solve\nsol = solve(prob, Tsit5(), saveat=0.1); ## Solve the problem and save at 0.1ms resolution.\nnothing #hide","category":"page"},{"location":"tutorials/ping_network/#Plotting-the-results","page":"Pyramidal-Interneuron Gamma network","title":"Plotting the results","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Now that we have a whole simulation, let's plot the results and see how they line up with the original figures. We're looking to reproduce the dynamics shown in Figure 1 of Börgers et al. [1]. To create raster plots in Neuroblox for the excitatory and inhibitory populations, it is as simple as:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"fig = Figure()\nrasterplot(fig[1,1], exci, sol; threshold=20.0, title=\"Excitatory Neurons\")\nrasterplot(fig[2,1], inhib, sol; threshold=20.0, title=\"Inhibitory Neurons\")\nfig","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"The upper panel should show the dynamics in Figure 1.C, with a clear population of excitatory neurons firing together from the external driving current, and the other excitatory neurons exhibiting more stochastic bursts. The lower panel should show the dynamics in Figure 1.A, with the inhibitory neurons firing in a more synchronous manner than the excitatory neurons.","category":"page"},{"location":"tutorials/ping_network/#Conclusion","page":"Pyramidal-Interneuron Gamma network","title":"Conclusion","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"And there you have it! A complete PING demonstration that reproduces the dynamics of a published paper in a matter of 30 seconds, give or take. Have fun making your own!","category":"page"},{"location":"tutorials/ping_network/#For-further-exploration","page":"Pyramidal-Interneuron Gamma network","title":"For further exploration","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"If you want to explore the PING network further, you can try the following:","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"You might notice that the excitatory and inhibitory populations become slightly desynchronized by the end of the simulation, unlike in the original paper. This is because of slight differences in how we implement the excitatory drive and inhibitory bath, which adjusts the overall E/I balance. Try increasing the inhibitory bath or decreasing the percentage of excitatory neurons that receive input and see how this affects the synchrony!\nThis tutorial is written entirely using built-in blocks for the excitatory and inhibitory neurons. If you want to explore the details, try typing PINGNeuronExci or PINGNeuronInhib in your Julia REPL to see the full details of the blocks. If you really want to dig into the details, type edit PINGNeuronExci() to open the source code and see how the equations are written.","category":"page"},{"location":"tutorials/ping_network/#References","page":"Pyramidal-Interneuron Gamma network","title":"References","text":"","category":"section"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"Börgers C, Epstein S, Kopell NJ. Gamma oscillations mediate stimulus competition and attentional selection in a cortical network model. Proc Natl Acad Sci U S A. 2008 Nov 18;105(46):18023-8. DOI: 10.1073/pnas.0809511105.\nTraub, RD, Miles, R. Neuronal Networks of the Hippocampus. Cambridge University Press, Cambridge, UK, 1991. DOI: 10.1017/CBO9780511895401\nWang, X-J, Buzsáki, G. Gamma oscillation by synaptic inhibition in a hippocampal interneuronal network model. J. Neurosci., 16:6402–6413, 1996. DOI: 10.1523/JNEUROSCI.16-20-06402.1996","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"","category":"page"},{"location":"tutorials/ping_network/","page":"Pyramidal-Interneuron Gamma network","title":"Pyramidal-Interneuron Gamma network","text":"This page was generated using Literate.jl.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"EditURL = \"basal_ganglia.jl\"","category":"page"},{"location":"tutorials/basal_ganglia/#Basal-Ganglia-Model-and-Parkinson's-Disease-Simulation","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"","category":"section"},{"location":"tutorials/basal_ganglia/#Introduction","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Introduction","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"This tutorial demonstrates how to build and simulate a basal ganglia model using Neuroblox, based on the work of Adam et al. (2022). We'll explore the model's behavior in both normal and Parkinsonian conditions, showcasing the emergence of pathological beta oscillations characteristic of Parkinson's disease.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"(Image: Full basal ganglia model in baseline condition)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"In previous tutorials, we explored building neural circuits from individual neuron bloxs and creating networks using neural mass bloxs. Here, we'll demonstrate how Neuroblox enables modeling of complex brain structures using specialized composite bloxs that encapsulate entire neural populations. These composite bloxs represent distinct neuronal populations within the basal ganglia, each containing multiple Hodgkin-Huxley neurons with their characteristic properties and intrinsic connectivity patterns.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"We'll start with simple components and gradually build up to the full basal ganglia circuit, demonstrating how to analyze the results at each stage.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"using Neuroblox\nusing StochasticDiffEq ## For building and solving differential equations problems\nusing CairoMakie ## For plotting\nusing Random ## For setting a random seed\n\nRandom.seed!(123) ## Set a random seed for reproducibility","category":"page"},{"location":"tutorials/basal_ganglia/#Isolated-MSN-network-in-baseline-condition","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Isolated MSN network in baseline condition","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"We'll start by simulating an isolated network of Medium Spiny Neurons (MSNs)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Blox definition","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"N_MSN = 100 ## number of Medium Spiny Neurons\n@named msn = Striatum_MSN_Adam(N_inhib = N_MSN)\nsys = structural_simplify(get_system(msn))\n\n# Check the system's variables (100 neurons, each with associated currents)\nunknowns(sys)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Create and solve the SDE problem","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"# Define simulation parameters\ntspan = (0.0, 2000.0) ## simulation time span [ms]\ndt = 0.05 ## time step for solving and saving [ms]\n\n# Create a stochastic differential equation problem and use the RKMil method to solve it\nprob = SDEProblem(sys, [], tspan, [])\nsol = solve(prob, RKMil(), dt = dt, saveat = dt);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Plot voltage of a single neuron","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"v = voltage_timeseries(msn, sol)\nfig = Figure()\nax = Axis(fig[1,1]; xlabel = \"Time (ms)\", ylabel = \"Voltage (mv)\")\nlines!(ax, sol.t, v[:, 1])\nfig ## to display the figure","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Plot mean field","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"meanfield(msn, sol, title = \"Mean Field Potential\")","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute firing rate, discarding the first 200 ms","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fr = firing_rate(msn, sol, threshold=-35, transient=200)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Create a raster plot","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"rasterplot(msn, sol, threshold = -35, title = \"Neuron's Spikes - Mean Firing Rate: $(round(fr[1], digits=2)) spikes/s\")","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute and plot the power spectrum of the GABAa current","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fig = Figure(size = (1500, 500))\n\npowerspectrumplot(fig[1,1], msn, sol, state = \"G\",\n title = \"FFT with no window\")\n\npowerspectrumplot(fig[1,2], msn, sol, state = \"G\",\n method = welch_pgram, window = hanning,\n title = \"Welch's method + Hanning window\")\nfig","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"We can leverage parallel computing using EnsembleProblem() to run multiple simulations simultaneously. This allows us to evaluate model outputs with multiple realizations of random physiological noise while utilizing all available computational threads (default) or processes.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"ens_prob = EnsembleProblem(prob)\nens_sol = solve(ens_prob, RKMil(), dt=dt, saveat=dt, trajectories = 3);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute average power spectrum","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"powerspectrumplot(msn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n title = \"Welch's method + Hanning window + Ensemble\")","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Note the peak at ~12 Hz in the MSN population's activity, representing an emergent beta-band oscillation characteristic of this circuit. We'll explore how this rhythm is altered by FSI inhibition next.","category":"page"},{"location":"tutorials/basal_ganglia/#Core-striatal-network:-MSN-FSI","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Core striatal network: MSN + FSI","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Now we'll add Fast-Spiking Interneurons (FSIs) to our model","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"global_ns = :g ## global name for the circuit. All components should be inside this namespace.\n\nN_FSI = 50 ## number of Fast Spiking Interneurons\n@named msn = Striatum_MSN_Adam(namespace = global_ns, N_inhib = N_MSN)\n@named fsi = Striatum_FSI_Adam(namespace = global_ns, N_inhib = N_FSI)\n\nḡ_FSI_MSN = 0.6 ## maximal conductance for FSI to MSN synapses [mS/cm^-2]\ndensity_FSI_MSN = 0.15 ## fraction of FSIs connecting to the MSN population\nweight_FSI_MSN = ḡ_FSI_MSN / (N_FSI * density_FSI_MSN) ## normalized synaptic weight\n\ng = MetaDiGraph()\nadd_edge!(g, fsi => msn, weight = weight_FSI_MSN, density = density_FSI_MSN)\n\n@named sys = system_from_graph(g)\nprob = SDEProblem(sys, [], tspan, [])\nens_prob = EnsembleProblem(prob)\nens_sol = solve(ens_prob, RKMil(), dt=dt, saveat = dt, trajectories = 3);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute firing rates for comparison","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fr_msn = firing_rate(msn, ens_sol[1], threshold=-35, transient=200)\nfr_fsi = firing_rate(fsi, ens_sol[1], threshold=-35, transient=200)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Let's see their raster plots and power spectra","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fig = Figure(size = (1000, 800))\nrasterplot(fig[1,1], msn, ens_sol[1], threshold = -35, title = \"MSN - Mean Firing Rate: $(round(fr_msn[1], digits=2)) spikes/s\")\nrasterplot(fig[1,2], fsi, ens_sol[1], threshold = -35, title = \"FSI - Mean Firing Rate: $(round(fr_fsi[1], digits=2)) spikes/s\")\n\npowerspectrumplot(fig[2,1], msn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims= (-35, 15),\n xlims= (8, 100))\n\npowerspectrumplot(fig[2,2], fsi, ens_sol, state = \"G\",\n method=welch_pgram, window=hanning,\n ylims= (-35, 15),\n xlims= (8, 100))\nfig","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"FSIs exhibit a peak in gamma frequencies. Their inhibition onto MSNs suppresses the low beta-band activity seen in isolated MSN populations, without reducing MSN firing rates. This spectral shift reflects a change in MSN spiking dynamics under FSI influence, rather than a decrease in overall activity.","category":"page"},{"location":"tutorials/basal_ganglia/#Full-basal-ganglia-model-in-baseline-condition","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Full basal ganglia model in baseline condition","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Now we'll add the GPe and STN to complete the full basal ganglia model","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"N_GPe = 80 ## number of GPe neurons\nN_STN = 40 ## number of STN neurons\n\n@named gpe = GPe_Adam(namespace = global_ns, N_inhib = N_GPe)\n@named stn = STN_Adam(namespace = global_ns, N_exci = N_STN)\n\nḡ_MSN_GPe = 2.5 ## maximal conductance for MSN to GPe synapses [mS/cm^-2]\nḡ_GPe_STN = 0.3 ## maximal conductance for GPe to STN synapses [mS/cm^-2]\nḡ_STN_FSI = 0.165 ## maximal conductance for STN to FSI synapses [mS/cm^-2]\n\ndensity_MSN_GPe = 0.33 ## fraction of MSNs connecting to the GPe population\ndensity_GPe_STN = 0.05 ## fraction of GPe neurons connecting to the STN population\ndensity_STN_FSI = 0.1 ## fraction of STN neurons connecting to the FSI population\n\nweight_MSN_GPe = ḡ_MSN_GPe / (N_MSN * density_MSN_GPe)\nweight_GPe_STN = ḡ_GPe_STN / (N_GPe * density_GPe_STN)\nweight_STN_FSI = ḡ_STN_FSI / (N_STN * density_STN_FSI)\n\ng = MetaDiGraph()\nadd_edge!(g, fsi => msn, weight = weight_FSI_MSN, density = density_FSI_MSN)\nadd_edge!(g, msn => gpe, weight = weight_MSN_GPe, density = density_MSN_GPe)\nadd_edge!(g, gpe => stn, weight = weight_GPe_STN, density = density_GPe_STN)\nadd_edge!(g, stn => fsi, weight = weight_STN_FSI, density = density_STN_FSI)\n\n@named sys = system_from_graph(g)\nprob = SDEProblem(sys, [], tspan, [])\nens_prob = EnsembleProblem(prob)\nens_sol = solve(ens_prob, RKMil(), dt=dt, saveat = dt, trajectories = 3);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute and plot power spectra for all components","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"fig = Figure(size = (1600, 450))\n\npowerspectrumplot(fig[1,1], msn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"MSN (Baseline)\")\n\npowerspectrumplot(fig[1,2], fsi, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"FSI (Baseline)\")\n\npowerspectrumplot(fig[1,3], gpe, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"GPe (Baseline)\")\n\npowerspectrumplot(fig[1,4], stn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"STN (Baseline)\")\n\nfig","category":"page"},{"location":"tutorials/basal_ganglia/#Full-basal-ganglia-model-in-Parkinson's-condition","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Full basal ganglia model in Parkinson's condition","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Finally, we'll adjust the model parameters to simulate Parkinson's disease conditions","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"(Image: Full basal ganglia model in Parkinsonian conditions)","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"The key changes from baseline to Parkinsonian conditions are:","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"For MSNs:\nIncreased background excitation (I_bg) to 1.2519 μA·cm^-2\nDecreased maximal conductance for M-current (G_M) to 1.2 mS·cm^-2\nFor FSIs:\nDecreased background excitation (I_bg) to 4.511 μA·cm^-2\nDecreased maximal conductance of FSI-MSN projection (ḡ_FSI_MSN) by 20% to 0.48 mS·cm^-2, due to increased cholinergic tone\nDecreased maximal conductance of FSI-FSI projection (weight) to 0.2 mS·cm^-2\nDecreased electrical conductance (g_elec) to 0.075","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"These changes reflect the loss of dopamine and increase in cholinergic tone characteristic of Parkinson's disease.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Create bloxs with Parkinsonian parameters","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"@named msn = Striatum_MSN_Adam(namespace = global_ns, N_inhib = N_MSN, I_bg = 1.2519*ones(N_MSN), G_M = 1.2)\n@named fsi = Striatum_FSI_Adam(namespace = global_ns, N_inhib = N_FSI, I_bg = 4.511*ones(N_FSI), weight = 0.2, g_weight = 0.075)\n\nḡ_FSI_MSN = 0.48 ## decreased maximal conductance of FSI-MSN projection [mS/cm^-2]\nweight_FSI_MSN = ḡ_FSI_MSN / (N_FSI * density_FSI_MSN) ## normalized synaptic weight\n\ng = MetaDiGraph()\nadd_edge!(g, fsi => msn, weight = weight_FSI_MSN, density = density_FSI_MSN)\nadd_edge!(g, msn => gpe, weight = weight_MSN_GPe, density = density_MSN_GPe)\nadd_edge!(g, gpe => stn, weight = weight_GPe_STN, density = density_GPe_STN)\nadd_edge!(g, stn => fsi, weight = weight_STN_FSI, density = density_STN_FSI)\n\n@named sys = system_from_graph(g)\n\nprob = SDEProblem(sys, [], tspan, [])\nens_prob = EnsembleProblem(prob)\nens_sol = solve(ens_prob, RKMil(), dt = dt, saveat = dt, trajectories = 3);\nnothing #hide","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Compute and compare power spectra for all neural populations in Parkinsonian condition against their counterparts in baseline conditions.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"powerspectrumplot(fig[2,1], msn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"MSN (PD)\")\n\npowerspectrumplot(fig[2,2], fsi, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"FSI (PD)\")\n\npowerspectrumplot(fig[2,3], gpe, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"GPe (PD)\")\n\npowerspectrumplot(fig[2,4], stn, ens_sol, state = \"G\",\n method = welch_pgram, window = hanning,\n ylims=(-40, 25),\n title = \"STN (PD)\")\n\nresize!(fig.scene, (1600, 900))\nfig","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"We see the emergence of strong beta oscillations in the Parkinsonian condition compared to the baseline condition for all neural populations. This aligns with the findings of Adam et al. and reflects the pathological synchrony observed in Parkinson's disease.","category":"page"},{"location":"tutorials/basal_ganglia/#References","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"References","text":"","category":"section"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"Adam, Elie M., et al. \"Deep brain stimulation in the subthalamic nucleus for Parkinson's disease can restore dynamics of striatal networks.\" Proceedings of the National Academy of Sciences 119.19 (2022): e2120808119.","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"","category":"page"},{"location":"tutorials/basal_ganglia/","page":"Basal Ganglia Model and Parkinson's Disease Simulation","title":"Basal Ganglia Model and Parkinson's Disease Simulation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"EditURL = \"neural_assembly.jl\"","category":"page"},{"location":"tutorials/neural_assembly/#Bottom-up-construction-of-a-neural-assembly","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"","category":"section"},{"location":"tutorials/neural_assembly/#Introduction","page":"Bottom-up construction of a neural assembly","title":"Introduction","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"This tutorial goes through the process of building a neural assembly that is part of a larger model that performs category learning of images [1]. We will follow a bottom-up approach with these steps :","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"build a model of a single neuron\nexpand that model by connecting a few neurons into a local circuit\ndefine a \"winner-takes-all\" (WTA) circuit with lateral inhibition\nbuild a cortical block by connecting multiple WTAs together with feed-forward inhibition\nconnect the cortical block to a model of an ascending system\nadd a source of visual input (images) and a cortical block representing visual cortex to our model and simulate visual processing","category":"page"},{"location":"tutorials/neural_assembly/#Single-spiking-neuron-from-Hodgkin-Huxley-model","page":"Bottom-up construction of a neural assembly","title":"Single spiking neuron from Hodgkin-Huxley model","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"(Image: fig1)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Hodgkin-Huxley (HH) formalism to describe membrane potential of a single neuron","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":" beginalign\n C_mfracdVdt = -g_L(V-V_L) - g_Nam^3h(V-V_Na) -g_Kn^4(V-V_K) + I_in - I_syn \n fracdmdt = alpha_m(V)(1-m) + beta_m(V)m \n fracdhdt = alpha_h(V)(1-h) + beta_h(V)h \n fracdndt = alpha_n(V)(1-n) + beta_n(V)n\n endalign","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"using Neuroblox\nusing OrdinaryDiffEq ## to build the ODE problem and solve it, gain access to multiple solvers from this\nusing Random ## for generating random variables\nusing CairoMakie ## for customized plotting recipies for blox\nusing CSV ## to read data from CSV files\nusing DataFrames ## to format the data into DataFrames\nusing Downloads ## to download image stimuli files","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"define a single excitatory neuron 'blox' with steady input current I_bg = 0.5 microA/cm2","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"nn1 = HHNeuronExciBlox(name=Symbol(\"nrn1\"), I_bg=0.5)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"define graph and add the single neuron 'blox' as a single node into the graph","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"g = MetaDiGraph() ## defines a graph\nadd_blox!(g, nn1) ## adds the defined blocks into the graph","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"create an ODESystem from the graph","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"@named sys = system_from_graph(g)\nlength(unknowns(sys)) ## shows the number of variables in the simplified system","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"To solve the system, we first create an Ordinary Differential Equation Problem and then solve it over the tspan of (0,1e) using a Vern7() solver. The solution is saved every 0.1ms. The unit of time in Neuroblox is 1ms.","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"prob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1);\nnothing #hide","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"acessing the voltage timeseries from the neuron block and plotting the voltage","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"v = voltage_timeseries(nn1, sol)\n\nfig = Figure();\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Voltage (mv)\")\ncl = get_neuron_color(nn1) #specify color based on neuron type (excitatory/inhibitory)\nlines!(ax, sol.t, v, color=cl)\nfig ## to display the figure","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Suggestion : Try different values of input current 'I_bg' and run the entire code block to see the output activity","category":"page"},{"location":"tutorials/neural_assembly/#Connecting-three-neurons-through-synapses-to-make-a-local-feed-forward-circuit","page":"Bottom-up construction of a neural assembly","title":"Connecting three neurons through synapses to make a local feed-forward circuit","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"(Image: fig2)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"# While creating a system of multiple components (neurons in this case), each component should be defined within the same namespace. So first\n# we define a global namespace.\nglobal_namespace=:g\n\n# define three neurons, two excitatory and one inhibitory\n\nnn1 = HHNeuronExciBlox(name=Symbol(\"nrn1\"), I_bg=0.4, namespace=global_namespace)\nnn2 = HHNeuronInhibBlox(name=Symbol(\"nrn2\"), I_bg=0.1, namespace=global_namespace)\nnn3 = HHNeuronExciBlox(name=Symbol(\"nrn3\"), I_bg=1.4, namespace=global_namespace)\n\n# defien graph and connect the nodes with the edges (synapses in this case), with the synaptic 'weights' specified as arguments\ng = MetaDiGraph()\nadd_edge!(g, nn1 => nn2, weight = 1) ##connection from neuron 1 to neuron 2 (nn1 to nn2)\nadd_edge!(g, nn2 => nn3, weight = 0.2) ##connection from neuron 2 to neuron 3 (nn2 to nn3)\nadd_edge!(g, nn1 => nn3, weight = 0.5) ##connection from neuron 1 to neuron 3 (nn2 to nn3)\n\n# create an ODESystem from the graph and then solve it using an ODE solver\n@named sys = system_from_graph(g)\nprob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1);\n\n# plotting membrane voltage activity of all neurons in a stacked form\n\nstackplot([nn1,nn2,nn3], sol)\t## stackplot(, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Suggestion : Try different values of input currents 'I_bg' and connection weights. One can try different permutations of excitatory and inhibitory neurons.","category":"page"},{"location":"tutorials/neural_assembly/#Creating-a-lateral-inhibition-circuit-(the-\"winner-takes-all\"-circuit)-in-superficial-cortical-layer","page":"Bottom-up construction of a neural assembly","title":"Creating a lateral inhibition circuit (the \"winner-takes-all\" circuit) in superficial cortical layer","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"(Image: fig3)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\nN_exci = 5; ##number of excitatory neurons\n\nn_inh = HHNeuronInhibBlox(name = Symbol(\"inh\"), namespace=global_namespace, G_syn = 4.0, τ = 70) ##feedback inhibitory interneuron neuron\n\n##creating an array of excitatory pyramidal neurons\nn_excis = [HHNeuronExciBlox(\n name = Symbol(\"exci$i\"),\n namespace=global_namespace,\n G_syn = 3.0,\n τ = 5,\n I_bg = 5*rand(),\n ) for i = 1:N_exci]\n\ng = MetaDiGraph()\n\nfor i in Base.OneTo(N_exci)\n add_edge!(g, n_inh => n_excis[i], weight = 1.0)\n add_edge!(g, n_excis[i] => n_inh, weight = 1.0)\nend\n\n@named sys = system_from_graph(g)\nprob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1)\nstackplot(vcat(n_excis, n_inh), sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Suggestion : Instead of uniform random input current in each excitatory neuron, try different configurations (random or constant) of input currents I_bg for each neuron. One can vary the size of circuit by changing number of excitatory neurons.","category":"page"},{"location":"tutorials/neural_assembly/#Creating-lateral-inhibition-\"winner-take-all\"-circuit-(WTA)-blocks-from-the-inbuilt-functions-and-connecting-two-WTA-circuit-blocks","page":"Bottom-up construction of a neural assembly","title":"Creating lateral inhibition \"winner-take-all\" circuit (WTA) blocks from the inbuilt functions and connecting two WTA circuit blocks","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\nN_exci = 5 ##number of excitatory neurons in each WTA circuit\nwta1 = WinnerTakeAllBlox(name=Symbol(\"wta1\"), I_bg=5.0, N_exci=N_exci, namespace=global_namespace) ##for a single valued input current, each neuron of the WTA circuit will recieve a uniformly distributed random input from 0 to I_bg\nwta2 = WinnerTakeAllBlox(name=Symbol(\"wta2\"), I_bg=4.0, N_exci=N_exci, namespace=global_namespace)\n\ng = MetaDiGraph()\nadd_edge!(g, wta1 => wta2, weight=1, density=0.5) ##density keyword sets the connection probability from each excitatory neuron of source WTA circuit to each excitatory neuron of target WTA circuit\n\nsys = system_from_graph(g, name=global_namespace)\nprob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1)\n\nneuron_set = get_neurons([wta1, wta2]) ## extract neurons from a composite blocks\nstackplot(neuron_set,sol)","category":"page"},{"location":"tutorials/neural_assembly/#Creating-a-single-cortical-superficial-layer-block-by-connecting-multiple-WTA-circuits","page":"Bottom-up construction of a neural assembly","title":"Creating a single cortical superficial layer block by connecting multiple WTA circuits","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"This model is SCORT in [1] and looks like this (Image: fig4)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\nN_wta=10 ## number of WTA circuits\n# parameters\nN_exci=5 ##number of pyramidal neurons in each lateral inhibition (WTA) circuit\nG_syn_exci=3.0 ##maximal synaptic conductance in glutamatergic (excitatory) synapses\nG_syn_inhib=4.0 ## maximal synaptic conductance in GABAergic (inhibitory) synapses from feedback interneurons\nG_syn_ff_inhib=3.5 ## maximal synaptic conductance in GABAergic (inhibitory) synapses from feedforward interneurons\nI_bg=5.0 ##background input\ndensity=0.01 ##connection density between WTA circuits\n\n##creating array of WTA ciruits\nwtas = [WinnerTakeAllBlox(;\n name=Symbol(\"wta$i\"),\n namespace=global_namespace,\n N_exci=N_exci,\n G_syn_exci=G_syn_exci,\n G_syn_inhib=G_syn_inhib,\n I_bg = I_bg\n ) for i = 1:N_wta]\n\n##feed-forward interneurons (get input from other pyramidal cells and from the ascending system, largely controls the rhythm)\nn_ff_inh = HHNeuronInhibBlox(;\n name=Symbol(\"ff_inh\"),\n namespace=global_namespace,\n G_syn=G_syn_ff_inhib\n )\n\ng = MetaDiGraph()\n\n# connecting WTA circuits to each other with given connection density, and feedforward interneuron connects to each WTA circuit\nfor i in 1:N_wta\n for j in 1:N_wta\n if j != i\n add_edge!(g, wtas[i] => wtas[j], weight=1, density=density)\n end\n end\n add_edge!(g, n_ff_inh => wtas[i], weight=1)\nend\n\nsys = system_from_graph(g, name=global_namespace)\nprob = ODEProblem(sys, [], (0.0, 1000), [])\nsol = solve(prob, Vern7(), saveat=0.1)\n\nneuron_set = get_neurons(vcat(wtas, n_ff_inh)) ## extract neurons from a composite blocks\nstackplot(neuron_set, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Sugestion : try different connection densities and weights and see how it affects the population activity.","category":"page"},{"location":"tutorials/neural_assembly/#Connecting-the-cortical-superficial-layer-block-to-an-ascending-system-block","page":"Bottom-up construction of a neural assembly","title":"Connecting the cortical superficial layer block to an ascending system block","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Now we will expand on the SCORT block of the previous section by defining a block representing an ascending system (ASC1 in [1]) and then connecting the two blocks together.","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\n\n# define ascending system block using a Next Generation Neural Mass model as described in Byrne et. al. 2020.\n# the parameters are fixed to generate a 16 Hz modulating frequency in the cortical neurons\n@named ASC1 = NextGenerationEIBlox(;namespace=global_namespace, Cₑ=2*26,Cᵢ=1*26, Δₑ=0.5, Δᵢ=0.5, η_0ₑ=10.0, η_0ᵢ=0.0, v_synₑₑ=10.0, v_synₑᵢ=-10.0, v_synᵢₑ=10.0, v_synᵢᵢ=-10.0, alpha_invₑₑ=10.0/26, alpha_invₑᵢ=0.8/26, alpha_invᵢₑ=10.0/26, alpha_invᵢᵢ=0.8/26, kₑₑ=0.0*26, kₑᵢ=0.6*26, kᵢₑ=0.6*26, kᵢᵢ=0*26)\n\n# define the superficial layer cortical block using inbuilt function\n# Number if WTA circuits = N_wta=45; number of pyramidal neurons in each WTA circuit = N_exci = 5;\n@named CB = CorticalBlox(N_wta=10, N_exci=5, density=0.01, weight=1, I_bg_ar=7; namespace=global_namespace)\n\n# define graph and connect ASC1->CB\ng = MetaDiGraph()\nadd_edge!(g, ASC1 => CB, weight=44)\n\n# solve the system for time 0 to 1000 ms\nsys = system_from_graph(g, name=global_namespace)\nprob = ODEProblem(sys, [], (0.0, 1000), []) ## tspan = (0,1000)\nsol = solve(prob, Vern7(), saveat=0.1);\nnothing #hide","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"plot neuron time series","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"neuron_set = get_neurons(CB) ## extract neurons from a composite block like CorticalBlox\nn_neurons = 50 ## set number nof neurons to display in the stackplot\nstackplot(neuron_set[1:n_neurons], sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"plot the meanfield of all cortical block neurons (mean membrane voltage)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"mnv = meanfield_timeseries(CB, sol)\nfig = Figure();\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Meanfield voltage (mv)\")\nlines!(ax, sol.t, mnv)\nfig ## to display the figure","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"plot power spectrum of the meanfield (average over membrane potentials)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"powerspectrumplot(CB, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Notice the peak at 16 Hz, representing beta oscillations. Sugestion : try changing parameters of ASC1 to generate different cortical rhythms. See how the peak shifts in the powerspectrum","category":"page"},{"location":"tutorials/neural_assembly/#Creating-simulation-of-visual-stimulus-response-in-cortical-blocks","page":"Bottom-up construction of a neural assembly","title":"Creating simulation of visual stimulus response in cortical blocks","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"(Image: fig5) create cortical blocks for visual area cortex (VAC), anterior cortex (AC) and ascending system block (ASC1)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"global_namespace=:g\n# cortical blox\n@named VAC = CorticalBlox(N_wta=10, N_exci=5, density=0.01, weight=1,I_bg_ar=0; namespace=global_namespace)\n@named AC = CorticalBlox(N_wta=10, N_exci=5, density=0.01, weight=1,I_bg_ar=0; namespace=global_namespace)\n# ascending system blox, modulating frequency set to 16 Hz\n@named ASC1 = NextGenerationEIBlox(;namespace=global_namespace, Cₑ=2*26,Cᵢ=1*26, Δₑ=0.5, Δᵢ=0.5, η_0ₑ=10.0, η_0ᵢ=0.0, v_synₑₑ=10.0, v_synₑᵢ=-10.0, v_synᵢₑ=10.0, v_synᵢᵢ=-10.0, alpha_invₑₑ=10.0/26, alpha_invₑᵢ=0.8/26, alpha_invᵢₑ=10.0/26, alpha_invᵢᵢ=0.8/26, kₑₑ=0.0*26, kₑᵢ=0.6*26, kᵢₑ=0.6*26, kᵢᵢ=0*26)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"create an image source block which takes image data from a .csv file and gives input to visual cortex","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"image_set = CSV.read(Downloads.download(\"raw.githubusercontent.com/Neuroblox/NeurobloxDocsHost/refs/heads/main/data/image_example.csv\"), DataFrame) ## reading data into DataFrame format\nimage_sample = 2 ## set which image to input (from 1 to 1000)\n\n# define stimulus source blox\n# t_stimulus: how long the stimulus is on (in msec)\n# t_pause : how long th estimulus is off (in msec)\n@named stim = ImageStimulus(image_set[[image_sample], :]; namespace=global_namespace, t_stimulus=1000, t_pause=0);\nnothing #hide","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"plot the image that the visual cortex 'sees'","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"pixels = Array(image_set[image_sample, 1:end-1])## access the desired image sample from respective row\npixels = reshape(pixels, 15, 15)## reshape into 15 X 15 square image matrix\nheatmap(pixels,colormap = :gray1) #input image matrix seen as heatmap","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"assemble the blox into a graph and set connections with their keword arguments like connection weight and connection density","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"g = MetaDiGraph()\n\nadd_edge!(g, stim => VAC, weight=14)\nadd_edge!(g, ASC1 => VAC, weight=44)\nadd_edge!(g, ASC1 => AC, weight=44)\nadd_edge!(g, VAC => AC, weight=3, density=0.08)\n\n# define system and solve\nsys = system_from_graph(g, name=global_namespace)\nprob = ODEProblem(sys, [], (0.0, 1000), []) ## tspan = (0,1000)\nsol = solve(prob, Vern7(), saveat=0.1);\nnothing #hide","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Let us now plot neuron potentials, meanfield activity and powerspectrums for the VAC and AC blox. First we show the stackplot of voltage potentials from the first 10 neurons of VAC","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"VAC_neuron_set = get_neurons(VAC) ## extract neurons from VAC\nn_neurons = 40 ##number of neurons displayed. You can try incresing it.\nstackplot(VAC_neuron_set[1:n_neurons],sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"then we plot the meanfield potential out of all neurons within VAC","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"mnv = meanfield_timeseries(VAC, sol)\n\nfig = Figure();\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Voltage (mv)\")\nlines!(ax, sol.t, mnv)\nfig ## to display the figure","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Here is the powerspectrum from all neurons within VAC","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"powerspectrumplot(VAC, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Moving on to the AC blox, we first plot the voltage potential of its neurons","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"AC_neuron_set = get_neurons(AC) ## extract neurons from VAC\nn_neurons = 40\nstackplot(AC_neuron_set[1:n_neurons], sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"followed by the meanfield activity","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"mnv = meanfield_timeseries(AC, sol)\nfig = Figure();\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Voltage (mv)\")\nlines!(ax, sol.t, mnv)\nfig ## to display the figure","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"and finally the AC powerspectrum","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"powerspectrumplot(AC, sol)","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"Sugestion : Try changing the image samples and notice the change in the spatial firing patterns in VAC and AC neurons. One can make multiple cortical blocks simillar to AC and connect them in various connection topologies. All of them can directly or indirectly get input from VAC.","category":"page"},{"location":"tutorials/neural_assembly/#References","page":"Bottom-up construction of a neural assembly","title":"References","text":"","category":"section"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"[1] Pathak A., Brincat S., Organtzidis H., Strey H., Senneff S., Antzoulatos E., Mujica-Parodi L., Miller E., Granger R. Biomimetic model of corticostriatal micro-assemblies discovers new neural code., bioRxiv 2023.11.06.565902, 2024","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"","category":"page"},{"location":"tutorials/neural_assembly/","page":"Bottom-up construction of a neural assembly","title":"Bottom-up construction of a neural assembly","text":"This page was generated using Literate.jl.","category":"page"},{"location":"api/#API-Documentation","page":"API","title":"API Documentation","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Modules = [Neuroblox]","category":"page"},{"location":"api/#Neuroblox.BalloonModel","page":"API","title":"Neuroblox.BalloonModel","text":"Arguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nlnκ: logarithmic prefactor to signal decay H[1], set to 0 for standard parameter value.\nlnτ: logarithmic prefactor to transit time H[3], set to 0 for standard parameter value.\nlnϵ: logarithm of ratio of intra- to extra-vascular signal\n\nNB: the prefix ln of the variables u, ν, q as well as the parameters κ, τ denotes their transformation into logarithmic space to enforce their positivity. This transformation is considered in the derivates of the model equations below. \n\nCitations:\n\nStephan K E, Weiskopf N, Drysdale P M, Robinson P A, and Friston K J. Comparing Hemodynamic Models with DCM. NeuroImage 38, no. 3 (2007): 387–401. doi: 10.1016/j.neuroimage.2007.07.040\nHofmann D, Chesebro A G, Rackauckas C, Mujica-Parodi L R, Friston K J, Edelman A, and Strey H H. Leveraging Julia's Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed, 2023. doi: 10.1101/2023.10.27.564407\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.DBS-Tuple{}","page":"API","title":"Neuroblox.DBS","text":"DBS(; name, namespace=nothing, frequency=130.0, amplitude=2.5, pulse_width=0.066, \n offset=0.0, start_time=0.0, smooth=1e-4)\n\nCreate a continuous deep brain stimulation (DBS) stimulus with regular pulses.\n\nArguments:\n\nname: Name given to ODESystem object within the blox\nnamespace: Additional namespace above name if needed for inheritance\nfrequency: Pulse frequency in Hz \namplitude: Pulse amplitude in arbitrary units\npulse_width: Duration of each pulse in ms\noffset: Baseline value of the signal between pulses\nstart_time: Time delay before stimulation begins in ms\nsmooth: Smoothing parameter for pulse transitions, set to 0 for sharp transitions\n\nReturns a DBS stimulus blox that outputs square pulses with specified parameters.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.Generic2dOscillator","page":"API","title":"Neuroblox.Generic2dOscillator","text":"Generic2dOscillator(name, namespace, ...)\n\nThe Generic2dOscillator model is a generic dynamic system with two state\nvariables. The dynamic equations of this model are composed of two ordinary\ndifferential equations comprising two nullclines. The first nullcline is a\ncubic function as it is found in most neuron and population models; the\nsecond nullcline is arbitrarily configurable as a polynomial function up to\nsecond order. The manipulation of the latter nullcline's parameters allows\nto generate a wide range of different behaviours.\n\nEquations:\n\n```math\n \\begin{align}\n \\dot{V} &= d \\, \\tau (-f V^3 + e V^2 + g V + \\alpha W + \\gamma I) \\\\\n \\dot{W} &= \\dfrac{d}{\tau}\\,\\,(c V^2 + b V - \\beta W + a)\n \\end{align}\n```\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nOther parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds.\n\nCitations: FitzHugh, R., Impulses and physiological states in theoretical models of nerve membrane, Biophysical Journal 1: 445, 1961.\n\nNagumo et.al, An Active Pulse Transmission Line Simulating Nerve Axon, Proceedings of the IRE 50: 2061, 1962.\n\nStefanescu, R., Jirsa, V.K. Reduced representations of heterogeneous mixed neural networks with synaptic coupling. Physical Review E, 83, 2011.\n\nJirsa VK, Stefanescu R. Neural population modes capture biologically realistic large-scale network dynamics. Bulletin of Mathematical Biology, 2010.\n\nStefanescu, R., Jirsa, V.K. A low dimensional description of globally coupled heterogeneous neural networks of excitatory and inhibitory neurons. PLoS Computational Biology, 4(11), 2008).\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.HarmonicOscillator","page":"API","title":"Neuroblox.HarmonicOscillator","text":"HarmonicOscillator(name, namespace, ω, ζ, k, h)\n\nCreate a harmonic oscillator blox with the specified parameters.\nThe formal definition of this blox is:\n\nfracdxdt = y-(2*omega*zeta*x)+ k*(2pi)*(atan((sumjcn)h)\nfracdydt = -(omega^2)*x\n\nwhere ``jcn`` is any input to the blox.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nω: Base frequency. Note the default value is scaled to give oscillations in milliseconds to match other blocks.\nζ: Damping ratio.\nk: Gain.\nh: Threshold.\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.JansenRit","page":"API","title":"Neuroblox.JansenRit","text":"JansenRit(name, namespace, τ, H, λ, r, cortical, delayed)\n\nCreate a Jansen Rit blox as described in Liu et al.\nThe formal definition of this blox is:\n\nfracdxdt = y-frac2taux\nfracdydt = -fracxtau^2 + fracHtau frac2lambda1+textexp(-r*sumjcn) - lambda\n\nwhere jcn is any input to the blox.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nτ: Time constant. Defaults to 1 for cortical regions, 14 for subcortical.\nH: See equation for use. Defaults to 0.02 for both cortical and subcortical regions.\nλ: See equation for use. Defaults to 5 for cortical regions, 400 for subcortical.\nr: See equation for use. Defaults to 0.15 for cortical regions, 0.1 for subcortical.\ncortical: Boolean to determine whether to use cortical or subcortical parameters. Specifying any of the parameters above will override this.\ndelayed: Boolean to indicate whether states are delayed\n\nCitations:\n\nLiu C, Zhou C, Wang J, Fietkiewicz C, Loparo KA. The role of coupling connections in a model of the cortico-basal ganglia-thalamocortical neural loop for the generation of beta oscillations. Neural Netw. 2020 Mar;123:381-392. doi: 10.1016/j.neunet.2019.12.021.\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.JansenRitSPM12","page":"API","title":"Neuroblox.JansenRitSPM12","text":"Jansen-Rit model block for canonical micro circuit, analogous to the implementation in SPM12\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.KuramotoOscillator","page":"API","title":"Neuroblox.KuramotoOscillator","text":"KuramotoOscillator(name, namespace, ...)\n\nSimple implementation of the Kuramoto oscillator as described in the original paper [1].\nUseful for general models of synchronization and oscillatory behavior.\nThe general form of the Kuramoto oscillator is given by:\nEquations:\n\n```math\n \\begin{equation}\n \\dot{\\theta_i} = \\omega_i + \\frac{1}{N}\\sum_{j=1}^N{K_{i, j}\\text{sin}(\\theta_j - \\theta_i)}\n \\end{equation}\n```\n\nWhere this describes the connection between regions $i$ and $j$. An alternative form\nwhich includes a noise term for each region is also provided, taking the form:\n\n```math\n \\begin{equation}\n \\dot{\\theta_i} = \\omega_i + \\zeta dW_i \\frac{1}{N}\\sum_{j=1}^N{K_{i, j}\\text{sin}(\\theta_j - \\theta_i)}\n \\end{equation}\n```\n\nwhere $W_i$ is a Wiener process and $\\zeta_i$ is the noise strength.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nOther parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds. Default parameter values are taken from [2].\n\nCitations:\n\nKuramoto, Y. (1975). Self-entrainment of a population of coupled non-linear oscillators. In: Araki, H. (eds) International Symposium on Mathematical Problems in Theoretical Physics. Lecture Notes in Physics, vol 39. Springer, Berlin, Heidelberg. https://doi.org/10.1007/BFb0013365\nSermon JJ, Wiest C, Tan H, Denison T, Duchet B. Evoked resonant neural activity long-term dynamics can be reproduced by a computational model with vesicle depletion. Neurobiol Dis. 2024 Jun 14;199:106565. doi: 10.1016/j.nbd.2024.106565. Epub ahead of print. PMID: 38880431.\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.LarterBreakspear","page":"API","title":"Neuroblox.LarterBreakspear","text":"LarterBreakspear(name, namespace, ...)\n\nCreate a Larter Breakspear blox described in Endo et al. For a full list of the parameters used see the reference.\nIf you need to modify the parameters, see Chesebro et al. and van Nieuwenhuizen et al. for physiological ranges.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nOther parameters: See reference for full list. Note that parameters are scaled so that units of time are in milliseconds.\n\nCitations:\n\nEndo H, Hiroe N, Yamashita O. Evaluation of Resting Spatio-Temporal Dynamics of a Neural Mass Model Using Resting fMRI Connectivity and EEG Microstates. Front Comput Neurosci. 2020 Jan 17;13:91. doi: 10.3389/fncom.2019.00091.\nChesebro AG, Mujica-Parodi LR, Weistuch C. Ion gradient-driven bifurcations of a multi-scale neuronal model. Chaos Solitons Fractals. 2023 Feb;167:113120. doi: 10.1016/j.chaos.2023.113120. \nvan Nieuwenhuizen, H, Chesebro, AG, Polis, C, Clarke, K, Strey, HH, Weistuch, C, Mujica-Parodi, LR. Ketosis regulates K+ ion channels, strengthening brain-wide signaling disrupted by age. Preprint. bioRxiv 2023.05.10.540257; doi: https://doi.org/10.1101/2023.05.10.540257. \n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.OUBlox","page":"API","title":"Neuroblox.OUBlox","text":"Ornstein-Uhlenbeck process Blox\n\nvariables: x(t): value jcn: input parameters: τ: relaxation time \tμ: average value \tσ: random noise (variance of OU process is τ*σ^2/2) returns: an ODE System (but with brownian parameters)\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.PINGNeuronExci","page":"API","title":"Neuroblox.PINGNeuronExci","text":"PINGNeuronExci(name, namespace, C, g_Na, V_Na, g_K, V_K, g_L, V_L, I_ext, τ_R, τ_D)\n\nCreate an excitatory neuron from Borgers et al. (2008).\nThe formal definition of this blox is:\n\nfracdVdt = frac1C(-g_Na*m_infty^3*h*(V - V_Na) - g_K*n^4*(V - V_K) - g_L*(V - V_L) + I_ext + jcn)\nm_infty = fraca_m(V)a_m(V) + b_m(V)\nfracdndt = a_n(V)*(1 - n) - b_n(V)*n\nfracdhdt = a_h(V)*(1 - h) - b_h(V)*h\nfracdsdt = frac12*(1 + tanh(V10))*(frac1 - stau_R - fracstau_D)\n\nwhere jcn is any input to the blox. Note that this is a modified Hodgkin-Huxley formalism with an additional synaptic accumulation term. Synapses are added into the jcn term by connecting the postsynaptic neuron's voltage to the presynaptic neuron's output:\n\njcn = w*s*(V_E - V)\n\nwhere w is the weight of the synapse and V_E is the reversal potential of the excitatory synapse.\n\nInputs:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nC: Membrane capacitance (defaults to 1.0).\ng_Na: Sodium conductance (defaults to 100.0).\nV_Na: Sodium reversal potential (defaults to 50.0).\ng_K: Potassium conductance (defaults to 80.0).\nV_K: Potassium reversal potential (defaults to -100.0).\ng_L: Leak conductance (defaults to 0.1).\nV_L: Leak reversal potential (defaults to -67.0).\nI_ext: External current (defaults to 0.0).\nτ_R: Rise time of synaptic conductance (defaults to 0.2).\nτ_D: Decay time of synaptic conductance (defaults to 2.0).\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.PINGNeuronInhib","page":"API","title":"Neuroblox.PINGNeuronInhib","text":"PINGNeuronInhib(name, namespace, C, g_Na, V_Na, g_K, V_K, g_L, V_L, I_ext, τ_R, τ_D)\n\nCreate an inhibitory neuron from Borgers et al. (2008).\nThe formal definition of this blox is:\n\nfracdVdt = frac1C(-g_Na*m_infty^3*h*(V - V_Na) - g_K*n^4*(V - V_K) - g_L*(V - V_L) + I_ext + jcn)\nm_infty = fraca_m(V)a_m(V) + b_m(V)\nfracdndt = a_n(V)*(1 - n) - b_n(V)*n\nfracdhdt = a_h(V)*(1 - h) - b_h(V)*h\nfracdsdt = frac12*(1 + tanh(V10))*(frac1 - stau_R - fracstau_D)\n\nwhere jcn is any input to the blox. Note that this is a modified Hodgkin-Huxley formalism with an additional synaptic accumulation term. Synapses are added into the jcn term by connecting the postsynaptic neuron's voltage to the presynaptic neuron's output:\n\njcn = w*s*(V_I - V)\n\nwhere w is the weight of the synapse and V_I is the reversal potential of the inhibitory synapse.\n\nInputs:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nC: Membrane capacitance (defaults to 1.0).\ng_Na: Sodium conductance (defaults to 35.0).\nV_Na: Sodium reversal potential (defaults to 55.0).\ng_K: Potassium conductance (defaults to 9.0).\nV_K: Potassium reversal potential (defaults to -90.0).\ng_L: Leak conductance (defaults to 0.1).\nV_L: Leak reversal potential (defaults to -65.0).\nI_ext: External current (defaults to 0.0).\nτ_R: Rise time of synaptic conductance (defaults to 0.5).\nτ_D: Decay time of synaptic conductance (defaults to 10.0).\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.Striatum","page":"API","title":"Neuroblox.Striatum","text":"Subcortical blox\nall subcprtical blox used in cortico-striatal model are defined here\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.WilsonCowan","page":"API","title":"Neuroblox.WilsonCowan","text":"WilsonCowan(name, namespace, τ_E, τ_I, a_E, a_I, c_EE, c_IE, c_EI, c_II, θ_E, θ_I, η)\n\nCreate a standard Wilson Cowan blox.\nThe formal definition of this blox is:\n\nfracdEdt = frac-Etau_E + frac11 + textexp(-a_E*(c_EE*E - c_IE*I - theta_E + eta*(sumjcn))\nfracdIdt = frac-Itau_I + frac11 + exp(-a_I*(c_EI*E - c_II*I - theta_I)\n\nwhere jcn is any input to the blox.\n\nArguments:\n\nname: Name given to ODESystem object within the blox.\nnamespace: Additional namespace above name if needed for inheritance.\nOthers: See equation for use.\n\n\n\n\n\n","category":"type"},{"location":"api/#Neuroblox.WinnerTakeAllBlox","page":"API","title":"Neuroblox.WinnerTakeAllBlox","text":"WinnerTakeAllBlox\n\nCreates a winner-take-all local circuit found in neocortex, typically 5 pyramidal (excitatory) neurons send synapses to a single interneuron (inhibitory) and receive feedback inhibition from that interneuron.\n\n\n\n\n\n","category":"type"},{"location":"api/#LinearAlgebra.eigen-Union{Tuple{Array{ForwardDiff.Dual{T, P, np}, 2}}, Tuple{np}, Tuple{P}, Tuple{T}} where {T, P, np}","page":"API","title":"LinearAlgebra.eigen","text":"function LinearAlgebra.eigen(M::Matrix{Dual{T, P, np}}) where {T, P, np}\n\nDispatch of LinearAlgebra.eigen for dual matrices with complex numbers. Make the eigenvalue decomposition \namenable to automatic differentiation. To do so compute the analytical derivative of eigenvalues\nand eigenvectors. \n\nArguments:\n- `M`: matrix of type Dual of which to compute the eigenvalue decomposition. \n\nReturns:\n- `Eigen(evals, evecs)`: eigenvalue decomposition returned as type LinearAlgebra.Eigen\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.ARVTarget-NTuple{6, Any}","page":"API","title":"Neuroblox.ARVTarget","text":"ARVTarget Time series data is bandpass filtered and then the power spectrum is computed for a given time interval (control bin), returned as the average value of the power spectral density within a certain frequency band ([lb, ub]).\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.CDVTarget-NTuple{5, Any}","page":"API","title":"Neuroblox.CDVTarget","text":"CDVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians. Circular difference is quantified as the angle of circular_location.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.ControlError-NTuple{8, Any}","page":"API","title":"Neuroblox.ControlError","text":"ControlError Returns the control error (deviation of the actual value from the target value).\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.PDVTarget-NTuple{5, Any}","page":"API","title":"Neuroblox.PDVTarget","text":"PDVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians. Phase deviation is quantified as the angle difference between a given set of signals.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.PLVTarget-NTuple{6, Any}","page":"API","title":"Neuroblox.PLVTarget","text":"PLVTarget Time series data is bandpass filtered and hilbert-transformed. Phase angle is computed in radians.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.ProtocolDBS-Tuple{}","page":"API","title":"Neuroblox.ProtocolDBS","text":"ProtocolDBS(; name, namespace=nothing, frequency=130.0, amplitude=2.5,\n pulse_width=0.066, offset=0.0, start_time=0.0, smooth=1e-4,\n pulses_per_burst=10, bursts_per_block=12, \n pre_block_time=200.0, inter_burst_time=200.0)\n\nCreate a deep brain stimulation (DBS) stimulus consisting of a block of pulse bursts.\n\nArguments:\n\nname: Name given to ODESystem object within the blox\nnamespace: Additional namespace above name if needed for inheritance\nfrequency: Pulse frequency in Hz\namplitude: Pulse amplitude in arbitrary units \npulse_width: Duration of each pulse in ms\noffset: Baseline value of the signal between pulses\nstart_time: Time delay before stimulation begins in ms\nsmooth: Smoothing parameter for pulse transitions, set to 0 for sharp transitions\npulsesperburst: Number of pulses in each burst\nburstsperblock: Number of bursts in the block\npreblocktime: Time before the block starts in ms\ninterbursttime: Time between bursts in ms\n\nReturns a DBS stimulus blox that outputs a block of pulse bursts.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.addnontunableparams-Tuple{Any, Any}","page":"API","title":"Neuroblox.addnontunableparams","text":"function addnontunableparams(param, model)\n\nFunction adds parameters of a model that were not marked as tunable to a list of tunable parameters\nand respects the MTK ordering of parameters.\n\nArguments:\n- `paramlist`: parameters of an MTK system that were tagged as tunable\n- `sys`: MTK system\n\nReturns:\n- `completeparamlist`: complete parameter list of a system, including those that were not tagged as tunable\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.bandpassfilter-Tuple{}","page":"API","title":"Neuroblox.bandpassfilter","text":"bandpassfilter takes in time series data and bandpass filters it. It has the following inputs: data: time series data lb: minimum cut-off frequency ub: maximum cut-off frequency fs: sampling frequency order: filter order\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.boldsignal-Tuple{}","page":"API","title":"Neuroblox.boldsignal","text":"Arguments:\n\nname: Name given to ODESystem object within the blox.\nlnϵ : logarithm of ratio of intra- to extra-vascular signal\n\nNB: the prefix ln of the variables ν, q as well as the parameters ϵ denotes their transformation into logarithmic space to enforce their positivity.\n\nCitations:\n\nStephan K E, Weiskopf N, Drysdale P M, Robinson P A, and Friston K J. Comparing Hemodynamic Models with DCM. NeuroImage 38, no. 3 (2007): 387–401. doi: 10.1016/j.neuroimage.2007.07.040\nHofmann D, Chesebro A G, Rackauckas C, Mujica-Parodi L R, Friston K J, Edelman A, and Strey H H. Leveraging Julia's Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed, 2023. doi: 10.1101/2023.10.27.564407\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.complexwavelet","page":"API","title":"Neuroblox.complexwavelet","text":"complexwavelet creates a complex morlet wavelet by windowing a complex sine wave with a Gaussian taper. The morlet wavelet is a special case of a bandpass filter in which the frequency response is Gaussian-shaped. Convolution with a complex wavelet is equivalent to performing a Hilbert transform of a bandpass filtered signal.\n\nIt has the following inputs: data: time series data dt : data sampling rate lb : lower bound wavelet frequency (in Hz) ub : upper bound wavelet frequency (in Hz) a : amplitude of the Gaussian taper, default is 1 n : number of wavelet cycles of the Gaussian taper, defines the trade-off between temporal precision and frequency precision larger n gives better frequency precision at the cost of temporal precision default is 6 Hz m : x-axis offset, default is 0 num_wavelets : number of wavelets to create, default is 5\n\nAnd outputs: complex_wavelet : a family of complex morlet wavelets\n\n\n\n\n\n","category":"function"},{"location":"api/#Neuroblox.csd2mar-NTuple{4, Any}","page":"API","title":"Neuroblox.csd2mar","text":"This function converts a cross-spectral density (CSD) into a multivariate auto-regression (MAR) model. It first transforms the CSD into its cross-correlation function (Wiener-Kinchine theorem) and then computes the MAR model coefficients. csd : cross-spectral density matrix of size MxN; M: number of samples, N: number of cross-spectral dimensions (number of variables squared) w : frequencies dt : time step size p : number of time steps of auto-regressive model\n\nThis function returns coeff : array of length p of coefficient matrices of size sqrt(N)xsqrt(N) noise_cov : noise covariance matrix\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.csd_approx-NTuple{4, Any}","page":"API","title":"Neuroblox.csd_approx","text":"This function implements equation 2 of the spectral DCM paper, Friston et al. 2014 \"A DCM for resting state fMRI\".\nNote that nomenclature is taken from SPM12 code and it does not seem to coincide with the spectral DCM paper's nomenclature. \nFor instance, Gu should represent the spectral component due to external input according to the paper. However, in the code this represents\nthe hidden state fluctuations (which are called Gν in the paper).\nGn in the code corresponds to Ge in the paper, i.e. the observation noise. In the code global and local components are defined, no such distinction\nis discussed in the paper. In fact the parameter γ, corresponding to local component is not present in the paper.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.get_dynamic_states-Tuple{Any}","page":"API","title":"Neuroblox.get_dynamic_states","text":"function get_dynamic_states(sys)\n\nFunction extracts states from the system that are dynamic variables, \nget also indices of external inputs (u(t)) and measurements (like bold(t))\nArguments:\n- `sys`: MTK system\n\nReturns:\n- `sts`: states/unknowns of the system that are neither external inputs nor measurements, i.e. these are the dynamic states\n- `idx`: indices of these states\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.get_input_equations-Tuple{Union{Neuroblox.AbstractBlox, Neuroblox.ObserverBlox}}","page":"API","title":"Neuroblox.get_input_equations","text":"Returns the equations for all input variables of a system, \nassuming they have a form like : `sys.input_variable ~ ...`\nso only the input appears on the LHS.\n\nInput equations are namespaced by the inner namespace of blox\nand then they are returned. This way during system `compose` downstream,\nthe higher-level namespaces will be added to them.\n\nIf blox isa AbstractComponent, it is assumed that it contains a `connector` field,\nwhich holds a `Connector` object with all relevant connections \nfrom lower levels and this level.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.idft-Tuple{AbstractArray}","page":"API","title":"Neuroblox.idft","text":"Plain implementation of idft because AD dispatch versions for ifft don't work still!\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.inner_namespaceof-Tuple{Any}","page":"API","title":"Neuroblox.inner_namespaceof","text":"Returns the complete namespace EXCLUDING the outermost (highest) level.\nThis is useful for manually preparing equations (e.g. connections, see Connector),\nthat will later be composed and will automatically get the outermost namespace.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.learningrate-Tuple{Any, Any}","page":"API","title":"Neuroblox.learningrate","text":"This function computes learning rate. It has the following inputs: outcomes: vector of 1's and 0's for behavioral outcomes windows: number of windows to split the outcome data into And the following outputs: rate: the learning rate across each window\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.mar2csd-Tuple{Any, Any, Any}","page":"API","title":"Neuroblox.mar2csd","text":"This function converts multivariate auto-regression (MAR) model parameters to a cross-spectral density (CSD). A : coefficients of MAR model, array of length p, each element contains the regression coefficients for that particular time-lag. Σ : noise covariance matrix of MAR p : number of time lags freqs : frequencies at which to evaluate the CSD sf : sampling frequency\n\nThis function returns: csd : cross-spectral density matrix of size MxN; M: number of samples, N: number of cross-spectral dimensions (number of variables squared)\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.mar_ml-Tuple{Any, Any}","page":"API","title":"Neuroblox.mar_ml","text":"Maximum likelihood estimator of a multivariate, or vector auto-regressive model. y : MxN Data matrix where M is number of samples and N is number of dimensions p : time lag parameter, also called order of MAR model return values mar[\"A\"] : model parameters is a NxNxP tensor, i.e. one NxN parameter matrix for each time bin k ∈ {1,...,p} mar[\"Σ\"] : noise covariance matrix\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.params-Tuple{Connector}","page":"API","title":"Neuroblox.params","text":"Helper to merge delay and weight into a single vector\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.paramscoping-Tuple{}","page":"API","title":"Neuroblox.paramscoping","text":"function paramscoping(;tunable=true, kwargs...)\n\nScope arguments that are already a symbolic model parameter thereby keep the correct namespace \nand make those that are not yet symbolic a symbol.\nKeyword arguments are used, because parameter definition require names, not just values.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.phase_cos_blox-Union{Tuple{F}, Tuple{Any, Any, F}} where F","page":"API","title":"Neuroblox.phase_cos_blox","text":"phasecosblox is creating a cos with angular frequency ω and variable phase phaseinter has the following parameters: ω: angular frequency t: time phaseinter: a function that returns phase as a function of time and returns: the resulting value\n\nUsage: phaseint = phaseinter(0:0.1:50,phasedata) phaseout(t) = phasecosblox(0.1,t,phaseint) which is now a function of time and can be used in an input blox you can also use the dot operator to calculate time-series signal = phaseout.(collect(0:0.01:50))\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.phase_inter-Tuple{Any, Any}","page":"API","title":"Neuroblox.phase_inter","text":"phaseinter is creating a function that interpolates the phase data for any time given phaseinter has the following parameters: phaserange: a range, e.g. 0:0.1:50 which should reflect the time points of the data phasedata: phase at equidistant time points and returns: an function that returns an interpolated phase for t in range\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.phase_sin_blox-Union{Tuple{F}, Tuple{Any, Any, F}} where F","page":"API","title":"Neuroblox.phase_sin_blox","text":"phasesinblox is creating a sin with angular frequency ω and variable phase phaseinter has the following parameters: ω: angular frequency t: time phaseinter: a function that returns phase as a function of time and returns: the resulting value\n\nUsage: phaseint = phaseinter(0:0.1:50,phasedata) phaseout(t) = phasesinblox(0.1,t,phaseint) which is now a function of time and can be used in an input blox you can also use the dot operator to calculate time-series signal = phaseout.(collect(0:0.01:50))\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.phaseangle-Tuple{}","page":"API","title":"Neuroblox.phaseangle","text":"phaseangle takes in time series data, hilbert transforms it, and estimates the phase angle.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.random_initials-Tuple{ODESystem, Any}","page":"API","title":"Neuroblox.random_initials","text":"random_initials creates a vector of random initial conditions for an ODESystem that is composed of a list of blox. The function finds the initial conditions in the blox and then sets a random value in between range tuple given for that state.\n\nIt has the following inputs: odesys: ODESystem blox : list of blox\n\nAnd outputs: u0 : Float64 vector of initial conditions\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.sample_affect!-NTuple{4, Any}","page":"API","title":"Neuroblox.sample_affect!","text":"Non-symbolic, time-block-based way of `@register_symbolic sample_poisson(λ)`.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.setup_sDCM-NTuple{9, Any}","page":"API","title":"Neuroblox.setup_sDCM","text":"function setup_sDCM(data, stateevolutionmodel, initcond, csdsetup, priors, hyperpriors, indices)\n\nInterface function to performs variational inference to fit model parameters to empirical cross spectral density.\nThe current implementation provides a Variational Laplace fit (see function above `variationalbayes`).\n\nArguments:\n- `data` : dataframe with column names corresponding to the regions of measurement.\n- `model` : MTK model, including state evolution and measurement.\n- `initcond` : dictionary of initial conditions, numerical values for all states\n- `csdsetup` : dictionary of parameters required for the computation of the cross spectral density\n-- `dt` : sampling interval\n-- `freq` : frequencies at which to evaluate the CSD\n-- `p` : order parameter of the multivariate autoregression model\n- `priors` : dataframe of parameters with the following columns:\n-- `name` : corresponds to MTK model name\n-- `mean` : corresponds to prior mean value\n-- `variance` : corresponds to the prior variances\n- `hyperpriors` : dataframe of parameters with the following columns:\n-- `Πλ_pr` : prior precision matrix for λ hyperparameter(s)\n-- `μλ_pr` : prior mean(s) for λ hyperparameter(s)\n- `indices` : indices to separate model parameters from other parameters. Needed for the computation of AD gradient.\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.spm_logdet-Tuple{Any}","page":"API","title":"Neuroblox.spm_logdet","text":"function spm_logdet(M)\n\nSPM12 style implementation of the logarithm of the determinant of a matrix.\n\nArguments:\n- `M`: matrix\n\n\n\n\n\n","category":"method"},{"location":"api/#Neuroblox.system_from_graph","page":"API","title":"Neuroblox.system_from_graph","text":"system_from_graph(g::MetaDiGraph, p=Num[]; name, simplify=true, graphdynamics=false, kwargs...)\n\nTake in a MetaDiGraph g describing a network of neural structures (and optionally a vector of extra parameters p) and construct a System which can be used to construct various Problem types (i.e. ODEProblem) for use with DifferentialEquations.jl solvers.\n\nIf simplify is set to true (the default), then the resulting system will have structural_simplify called on it with any remaining keyword arguments forwared to structural_simplify. That is,\n\n@named sys = system_from_graph(g; kwarg1=x, kwarg2=y)\n\nis equivalent to\n\n@named sys = system_from_graph(g; simplify=false)\nsys = structural_simplify(sys; kwarg1=x, kwarg2=y)\n\nSee the docstring for structural_simplify for information on which options it supports.\n\nIf graphdynamics=true (defaults to false), the output will be a GraphSystem from GraphDynamics.jl, and the kwargs will be sent to the GraphDynamics constructor instead of using ModelingToolkit.jl. The GraphDynamics.jl backend is typically significantly faster for large neural systems than the default backend, but is experimental and does not yet support all Neuroblox.jl features. \n\n\n\n\n\n","category":"function"},{"location":"api/#Neuroblox.vecparam-Tuple{OrderedCollections.OrderedDict}","page":"API","title":"Neuroblox.vecparam","text":"vecparam(param::OrderedDict)\n\nFunction to flatten an ordered dictionary of model parameters and return a simple list of parameter values.\n\nArguments:\n- `param`: dictionary of model parameters (may contain numbers and lists of numbers)\n\n\n\n\n\n","category":"method"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"EditURL = \"spectralDCM.jl\"","category":"page"},{"location":"tutorials/spectralDCM/#Solving-Inverse-Problems-with-Spectral-Dynamic-Causal-Modeling","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"","category":"section"},{"location":"tutorials/spectralDCM/#Introduction","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Introduction","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Neuroblox provides you with a comprehensive environment for simulations as we have explored previously, but its functionality doesn't stop there. We will now pivot and turn our attention to a different kind of problem: inferring model parameters, that is solving inverse problems, from time series. The method of choice is one of the most widely spread in imaging neuroscience, spectral Dynamic Causal Modeling (spDCM)[1,2]. In this tutorial we will introduce how to perform a spDCM analysis on simulated data. To do so we roughly reproduce the procedure in the SPM12 script DEM_demo_induced_fMRI.m in Neuroblox. This work was also presented in Hofmann et al.[2]","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"In this tutorial we will define a circuit of three linear neuronal mass models, all driven by an Ornstein-Uhlenbeck process. We will model fMRI data by a balloon model and BOLD signal on top. After simulation of this simple model we will use spectral Dynamic Causal Modeling to infer some of the model parameters from the simulation time series.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"(Image: Workflow illustration)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"A brief outline of the procedure we will pursue:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"define the graph, add blocks -> section A, B and C in the figure\nsimulate the model -> instead we could also use actual data, section D in figure\ncompute the cross spectral density\nsetup the DCM\nestimate parameters\nplot the results","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"using Neuroblox\nusing LinearAlgebra\nusing StochasticDiffEq\nusing DataFrames\nusing OrderedCollections\nusing CairoMakie\nusing ModelingToolkit","category":"page"},{"location":"tutorials/spectralDCM/#Model-simulation","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Model simulation","text":"","category":"section"},{"location":"tutorials/spectralDCM/#Define-the-model","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Define the model","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We will define a model of 3 regions. This means first of all to define a graph. To this graph we will add three linear neuronal mass models which constitute the (hidden) neuronal dynamics. These constitute three nodes of the graph. Next we will also need some input that stimulates the activity, we use simple Ornstein-Uhlenbeck blocks to create stochastic inputs. One per region. We want to simulate fMRI signals thus we will need to also add a BalloonModel per region. Note that the Ornstein-Uhlenbeck block will feed into the linear neural mass which in turn will feed into the BalloonModel blox. This needs to be represented by the way we define the edges.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"nr = 3 # number of regions\ng = MetaDiGraph()\nregions = []; # list of neural mass blocks to then connect them to each other with an adjacency matrix `A_true`\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Now add the different blocks to each region and connect the blocks within each region:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"for i = 1:nr\n region = LinearNeuralMass(;name=Symbol(\"r$(i)₊lm\"))\n push!(regions, region) # store neural mass model for connection of regions\n\n # add Ornstein-Uhlenbeck block as noisy input to the current region\n input = OUBlox(;name=Symbol(\"r$(i)₊ou\"), σ=0.1)\n add_edge!(g, input => region, weight=1/16) # Note that 1/16 is taken from SPM12, this stabilizes the balloon model simulation. Alternatively the noise of the Ornstein-Uhlenbeck block or the weight of the edge connecting neuronal activity and balloon model could be reduced to guarantee numerical stability.\n\n # simulate fMRI signal with BalloonModel which includes the BOLD signal on top of the balloon model dynamics\n measurement = BalloonModel(;name=Symbol(\"r$(i)₊bm\"))\n add_edge!(g, region => measurement, weight=1.0)\nend","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Next we define the between-region connectivity matrix and make sure that it is diagonally dominant to guarantee numerical stability (see Gershgorin theorem).","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"A_true = 0.1*randn(nr, nr)\nA_true -= diagm(map(a -> sum(abs, a), eachrow(A_true))) # ensure diagonal dominance of matrix","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Instead of a random matrix use the same matrix as is defined in [3]","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"A_true = [[-0.5 -2 0]; [0.4 -0.5 -0.3]; [0 0.2 -0.5]]\nfor idx in CartesianIndices(A_true)\n add_edge!(g, regions[idx[1]] => regions[idx[2]], weight=A_true[idx[1], idx[2]])\nend","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"finally we compose the simulation model","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"@named simmodel = system_from_graph(g, split=false)","category":"page"},{"location":"tutorials/spectralDCM/#Run-the-simulation-and-plot-the-results","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Run the simulation and plot the results","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"setup simulation of the model, time in seconds","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"tspan = (0.0, 512.0)\nprob = SDEProblem(simmodel, [], tspan)\ndt = 2.0 # two seconds as measurement interval for fMRI\nsol = solve(prob, ImplicitRKMil(), saveat=dt);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"plot bold signal time series","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"idx_m = get_idx_tagged_vars(simmodel, \"measurement\") # get index of bold signal\nf = Figure()\nax = Axis(f[1, 1],\n title = \"fMRI time series\",\n xlabel = \"Time [s]\",\n ylabel = \"BOLD\",\n)\nlines!(ax, sol, idxs=idx_m)\nf","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We note that the initial spike is not meaningful and a result of the equilibration of the stochastic process thus we remove it.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"dfsol = DataFrame(sol[ceil(Int, 101/dt):end]);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/#Estimate-and-plot-the-cross-spectral-densities","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Estimate and plot the cross-spectral densities","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"data = Matrix(dfsol[:, idx_m]);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We compute the cross-spectral density by fitting a linear model of order p and then compute the csd analytically from the parameters of the multivariate autoregressive model","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"p = 8\nmar = mar_ml(data, p) # maximum likelihood estimation of the MAR coefficients and noise covariance matrix\nns = size(data, 1)\nfreq = range(min(128, ns*dt)^-1, max(8, 2*dt)^-1, 32)\ncsd = mar2csd(mar, freq, dt^-1);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Now plot the cross-spectrum:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"fig = Figure(size=(1200, 800))\ngrid = fig[1, 1] = GridLayout()\nfor i = 1:nr\n for j = 1:nr\n ax = Axis(grid[i, j])\n lines!(ax, freq, real.(csd[:, i, j]))\n end\nend\nfig","category":"page"},{"location":"tutorials/spectralDCM/#Model-Inference","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Model Inference","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We will now assemble a new model that is used for fitting the previous simulations. This procedure is similar to before with the difference that we will define global parameters and use tags such as [tunable=false/true] to define which parameters we will want to estimate. Note that parameters are tunable by default.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"g = MetaDiGraph()\nregions = []; # list of neural mass blocks to then connect them to each other with an adjacency matrix `A`\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"The following parameters are shared accross regions, which is why we define them here.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"@parameters lnκ=0.0 [tunable=false] lnϵ=0.0 [tunable=false] lnτ=0.0 [tunable=false] # lnκ: decay parameter for hemodynamics; lnϵ: ratio of intra- to extra-vascular components, lnτ: transit time scale\n@parameters C=1/16 [tunable=false] # note that C=1/16 is taken from SPM12 and stabilizes the balloon model simulation. See also comment above.\n\nfor i = 1:nr\n region = LinearNeuralMass(;name=Symbol(\"r$(i)₊lm\"))\n push!(regions, region)\n input = ExternalInput(;name=Symbol(\"r$(i)₊ei\"))\n add_edge!(g, input => region, weight=C)\n\n # we assume fMRI signal and model them with a BalloonModel\n measurement = BalloonModel(;name=Symbol(\"r$(i)₊bm\"), lnτ=lnτ, lnκ=lnκ, lnϵ=lnϵ)\n add_edge!(g, region => measurement, weight=1.0)\nend\n\nA_prior = 0.01*randn(nr, nr)\nA_prior -= diagm(diag(A_prior)) # remove the diagonal","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Since we want to optimize these weights we turn them into symbolic parameters: Add the symbolic weights to the edges and connect regions.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"A = []\nfor (i, a) in enumerate(vec(A_prior))\n symb = Symbol(\"A$(i)\")\n push!(A, only(@parameters $symb = a))\nend\n\nfor (i, idx) in enumerate(CartesianIndices(A_prior))\n if idx[1] == idx[2]\n add_edge!(g, regions[idx[1]] => regions[idx[2]], weight=-exp(A[i])/2) # -exp(A[i])/2: treatement of diagonal elements in SPM12 to make diagonal dominance (see Gershgorin Theorem) more likely but it is not guaranteed\n else\n add_edge!(g, regions[idx[2]] => regions[idx[1]], weight=A[i])\n end\nend","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"we avoid simplification of the model in order to exclude some parameters from fitting","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"@named fitmodel = system_from_graph(g, simplify=false)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"With the function changetune` we can provide a dictionary of parameters whose tunable flag should be changed, for instance set to false to exclude them from the optimizatoin procedure. For instance the the effective connections that are set to zero in the simulation:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"untune = Dict(A[3] => false, A[7] => false)\nfitmodel = changetune(fitmodel, untune) # 3 and 7 are not present in the simulation model\nfitmodel = structural_simplify(fitmodel, split=false) # and now simplify the euqations","category":"page"},{"location":"tutorials/spectralDCM/#Setup-spectral-DCM","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Setup spectral DCM","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"max_iter = 128; # maximum number of iterations\n# attribute initial conditions to states\nsts, _ = get_dynamic_states(fitmodel);\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"the following step is needed if the model's Jacobian would give degenerate eigenvalues if expanded around 0 (which is the default expansion)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"perturbedfp = Dict(sts .=> abs.(0.001*rand(length(sts)))) # slight noise to avoid issues with Automatic Differentiation. TODO: find different solution, this is hacky.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We can use the default prior function to use standardized prior values as given in SPM12.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"pmean, pcovariance, indices = defaultprior(fitmodel, nr)\n\npriors = (μθ_pr = pmean,\n Σθ_pr = pcovariance\n );\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Setup hyper parameter prior as well:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"hyperpriors = Dict(:Πλ_pr => 128.0*ones(1, 1), # prior metaparameter precision, needs to be a matrix\n :μλ_pr => [8.0] # prior metaparameter mean, needs to be a vector\n );\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"To compute the cross spectral densities we need to provide the sampling interval of the time series, the frequency axis and the order of the multivariate autoregressive model:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"csdsetup = (mar_order = p, freq = freq, dt = dt);\n\n_, s_bold = get_eqidx_tagged_vars(fitmodel, \"measurement\"); # get bold signal variables\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Prepare the DCM:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"(state, setup) = setup_sDCM(dfsol[:, String.(Symbol.(s_bold))], fitmodel, perturbedfp, csdsetup, priors, hyperpriors, indices, pmean, \"fMRI\");\n\n# HACK: on machines with very small amounts of RAM, Julia can run out of stack space while compiling the code called in this loop\n# this should be rewritten to abuse the compiler less, but for now, an easy solution is just to run it with more allocated stack space.\nwith_stack(f, n) = fetch(schedule(Task(f, n)));\nnothing #hide","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"We are ready to run the optimization procedure! :)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"with_stack(5_000_000) do # 5MB of stack space\n for iter in 1:max_iter\n state.iter = iter\n run_sDCM_iteration!(state, setup)\n print(\"iteration: \", iter, \" - F:\", state.F[end] - state.F[2], \" - dF predicted:\", state.dF[end], \"\\n\")\n if iter >= 4\n criterion = state.dF[end-3:end] .< setup.tolerance\n if all(criterion)\n print(\"convergence\\n\")\n break\n end\n end\n end\nend","category":"page"},{"location":"tutorials/spectralDCM/#Plot-Results","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Plot Results","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Plot the free energy evolution over optimization iterations:","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"freeenergy(state)","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"Plot the estimated posterior of the effective connectivity and compare that to the true parameter values. Bar hight are the posterior mean and error bars are the standard deviation of the posterior.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"ecbarplot(state, setup, A_true)","category":"page"},{"location":"tutorials/spectralDCM/#References","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"References","text":"","category":"section"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"[1] Novelli, Leonardo, Karl Friston, and Adeel Razi. “Spectral Dynamic Causal Modeling: A Didactic Introduction and Its Relationship with Functional Connectivity.” Network Neuroscience 8, no. 1 (April 1, 2024): 178–202. \n[2] Hofmann, David, Anthony G. Chesebro, Chris Rackauckas, Lilianne R. Mujica-Parodi, Karl J. Friston, Alan Edelman, and Helmut H. Strey. “Leveraging Julia’s Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed.” bioRxiv: The Preprint Server for Biology, 2023. \n[3] Friston, Karl J., Joshua Kahan, Bharat Biswal, and Adeel Razi. “A DCM for Resting State fMRI.” NeuroImage 94 (July 2014): 396–407.","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"","category":"page"},{"location":"tutorials/spectralDCM/","page":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","title":"Solving Inverse Problems with Spectral Dynamic Causal Modeling","text":"This page was generated using Literate.jl.","category":"page"},{"location":"release_notes/#Release-Notes","page":"Release Notes","title":"Release Notes","text":"","category":"section"},{"location":"release_notes/#v0.3","page":"Release Notes","title":"v0.3","text":"","category":"section"},{"location":"release_notes/","page":"Release Notes","title":"Release Notes","text":"Initial Release!","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"EditURL = \"getting_started.jl\"","category":"page"},{"location":"getting_started/#Getting-Started","page":"Getting Started","title":"Getting Started","text":"","category":"section"},{"location":"getting_started/#Getting-Started-with-Julia","page":"Getting Started","title":"Getting Started with Julia","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Here we would like to summarize some resources for people that are interested in learning more about the Julia language before or while exploring Neuroblox. Please follow the links below for introductory material on the language that is inclusive to all users; people familiar with programming or not, people with a mathematics, engineering, or science background :","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Introduction to Julia by Matt Bauman at the JuliaCon 2024\nJulia Tutorials & Workshops, a collection of training materials from the official Julia website.\nModern Julia Workflows, an introduction to how to write and share your Julia code effectively with tips & tricks.","category":"page"},{"location":"getting_started/#getting_started_julia","page":"Getting Started","title":"Getting Started with Neuroblox","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"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.","category":"page"},{"location":"getting_started/#The-Wilson-Cowan-Model","page":"Getting Started","title":"The Wilson-Cowan Model","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Each Wilson-Cowan neural mass is described by the following equations:","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"beginalign\nnonumber\nfracdEdt = frac-Etau_E + S_E(c_EEE - c_IEI + etatextstylesumjcn)10pt\nnonumber\nfracdIdt = frac-Itau_I + S_Ileft(c_EIE - c_IIIright)\nendalign","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"where E and I denote the activity levels of the excitatory and inhibitory populations, respectively. The terms fracdEdt and fracdIdt describe the rate of change of these activity levels over time. The parameters tau_E and tau_I are time constants analogous to membrane time constants in single neuron models, determining how quickly the excitatory and inhibitory populations respond to changes. The coefficients c_EE and c_II represent self-interaction (or feedback) within excitatory and inhibitory populations, while c_IE and c_EI represent the cross-interactions between the two populations. The term etasumjcn represents external input to the excitatory population from other brain regions or external stimuli, with eta acting as a scaling factor. While S_E and S_I are sigmoid functions that represent the responses of neuronal populations to input stimuli, defined as:","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"S_k(x) = frac11 + exp(-a_kx - theta_k)","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"where a_k and theta_k determine the steepness and threshold of the response, respectively.","category":"page"},{"location":"getting_started/#Building-the-Circuit","page":"Getting Started","title":"Building the Circuit","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Let's create an oscillating circuit by connecting two Wilson-Cowan neural masses:","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"using Neuroblox\nusing OrdinaryDiffEq\nusing CairoMakie\n\n# Create two Wilson-Cowan blox\n@named WC1 = WilsonCowan()\n@named WC2 = WilsonCowan()\n\n# Create a graph to represent our circuit\ng = MetaDiGraph()\nadd_blox!.(Ref(g), [WC1, WC2])\n\n# Define the connectivity between the neural masses\nadd_edge!(g, WC1 => WC1; weight = -1) ## recurrent connection from WC1 to itself\nadd_edge!(g, WC1 => WC2; weight = 7) ## connection from WC1 to WC2\nadd_edge!(g, WC2 => WC1; weight = 4) ## connection from WC2 to WC1\nadd_edge!(g, WC2 => WC2; weight = -1) ## recurrent connection from WC2 to itself","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"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.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"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.","category":"page"},{"location":"getting_started/#Creating-the-Model","page":"Getting Started","title":"Creating the Model","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"Now, let's build the complete model:","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"@named sys = system_from_graph(g)","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"This creates a differential equations system from our graph representation using ModelingToolkit and symbolically simplifies it for efficient computation.","category":"page"},{"location":"getting_started/#Simulating-the-Model","page":"Getting Started","title":"Simulating the Model","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"We are now ready to simulate our model. The following code creates and solves an ODEProblem for our system, simulating 100 time units of activity. In Neuroblox, the default time unit is milliseconds. We use Rodas4, a solver efficient for stiff problems. The solution is saved every 0.1 ms, allowing us to observe the detailed evolution of the system's behavior.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"prob = ODEProblem(sys, [], (0.0, 100), [])\nsol = solve(prob, Rodas4(), saveat=0.1)","category":"page"},{"location":"getting_started/#Plotting-simulation-results","page":"Getting Started","title":"Plotting simulation results","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"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.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"E1 = state_timeseries(WC1, sol, \"E\")\nI1 = state_timeseries(WC1, sol, \"I\")\n\nfig = Figure()\nax = Axis(fig[1,1]; xlabel = \"time (ms)\")\nlines!(ax, sol.t, E1, label = \"E\")\nlines!(ax, sol.t, I1, label = \"I\")\nLegend(fig[1,2], ax)\nfig","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"[1] Wilson, H. R., & Cowan, J. D. (1972). Excitatory and inhibitory interactions in localized populations of model neurons. Biophysical journal, 12(1), 1-24.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"[2] Destexhe, A., & Sejnowski, T. J. (2009). The Wilson–Cowan model, 36 years later. Biological cybernetics, 101(1), 1-2.","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"","category":"page"},{"location":"getting_started/","page":"Getting Started","title":"Getting Started","text":"This page was generated using Literate.jl.","category":"page"},{"location":"#Neuroblox","page":"Neuroblox","title":"Neuroblox","text":"","category":"section"},{"location":"#About","page":"Neuroblox","title":"About","text":"","category":"section"},{"location":"","page":"Neuroblox","title":"Neuroblox","text":"Neuroblox.jl is designed for computational neuroscience and psychiatry applications. Our tools range from control circuit system identification to brain circuit simulations bridging scales from spiking neurons to fMRI-derived circuits, parameter-fitting models to neuroimaging data, interactions between the brain and other physiological systems, experimental optimization, and scientific machine learning.","category":"page"},{"location":"#Description","page":"Neuroblox","title":"Description","text":"","category":"section"},{"location":"","page":"Neuroblox","title":"Neuroblox","text":"Neuroblox.jl is based on a library of modular computational building blocks (“blox”) in the form of systems of symbolic dynamic differential equations that can be combined to describe large-scale brain dynamics. Once a model is built, it can be simulated efficiently and fit electrophysiological and neuroimaging data. Moreover, the circuit behavior of multiple model variants can be investigated to aid in distinguishing between competing hypotheses. We employ ModelingToolkit.jl to describe the dynamical behavior of blox as symbolic (stochastic/delay) differential equations. Our libraries of modular blox consist of individual neurons (Hodgkin-Huxley, IF, QIF, LIF, etc.), neural mass models (Jansen-Rit, Wilson-Cowan, Lauter-Breakspear, Next Generation, microcanonical circuits etc.) and biomimetically-constrained control circuit elements. A GUI designed to be intuitive to neuroscientists allows researchers to build models that automatically generate high-performance systems of numerical ordinary/stochastic differential equations from which one can run stimulations with parameters fit to experimental data. Our benchmarks show that the increase in speed for simulation often exceeds a factor of 100 as compared to neural mass model implementation by the Virtual Brain (python) and similar packages in MATLAB. For parameter fitting of brain circuit dynamical models, we use Turing.jl to perform probabilistic modeling, including Hamilton-Monte-Carlo sampling and Automated Differentiation Variational Inference.","category":"page"},{"location":"#Licensing","page":"Neuroblox","title":"Licensing","text":"","category":"section"},{"location":"","page":"Neuroblox","title":"Neuroblox","text":"Neuroblox is free for non-commerical and academic use. For full details of the license, please see the Neuroblox EULA. For commercial use, get in contact with sales@neuroblox.org.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"EditURL = \"resting_state.jl\"","category":"page"},{"location":"tutorials/resting_state/#Resting-state-simulation-using-neural-mass-models","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"","category":"section"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"This tutorial will introduce you to simulating resting state brain dynamics using Neuroblox. We will be using the FitzHugh-Nagumo model as a building block. The FitzHugh-Nagumo model is described by the follwoing equations:","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":" beginalign\n dotV = d tau (-f V^3 + e V^2 + alpha W - gamma I_c + sigma w(t) ) \n dotW = dfracdtau(b V - beta W + a + sigma w(t) )\n endalign","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"We start by building the resting state circuit from individual Generic2dOscillator Blox","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"using Neuroblox\nusing CSV\nusing DataFrames\nusing StochasticDiffEq\nusing Random\nusing CairoMakie\nusing Statistics\nusing HypothesisTests\nusing Downloads\nusing SparseArrays\n\nRandom.seed!(123); # Set a fixed RNG seed for reproducible results\n\n# download and read connection matrix from a file\nweights = CSV.read(Downloads.download(\"raw.githubusercontent.com/Neuroblox/NeurobloxDocsHost/refs/heads/main/data/weights.csv\"), DataFrame)\nregion_names = names(weights)\n\nwm = Matrix(weights) ## transform the weights into a matrix\nN_bloxs = size(wm)[1]; ## number of blox components\nnothing #hide","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"You can visualize the sparsity structure by converting the weights matrix into a sparse matrix","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"wm_sparse = SparseMatrixCSC(wm)","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"After the connectivity structure, it's time to define the neural mass components of our model and then use the weight matrix to connect them together into our final system.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"# create an array of neural mass models\nblox = [Generic2dOscillator(name=Symbol(region_names[i]),bn=sqrt(5e-4)) for i in 1:N_bloxs]\n\n# add neural mass models to Graph and connect using the connection matrix\ng = MetaDiGraph()\nadd_blox!.(Ref(g), blox)\ncreate_adjacency_edges!(g, wm)\n\n@named sys = system_from_graph(g);\nnothing #hide","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"To solve the system, we first create an Stochastic Differential Equation Problem and then solve it using a EulerHeun solver. The solution is saved every 0.5 ms. The unit of time in Neuroblox is 1 ms.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"tspan = (0.0, 6e5)\nprob = SDEProblem(sys,rand(-2:0.1:4,76*2), tspan, [])\nsol = solve(prob, EulerHeun(), dt=0.5, saveat=5);\nnothing #hide","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"Let us now plot the voltage potential of the first couple of components. We can extract the voltage timeseries of a blox from the solution object using the voltage_timeseries function.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"v1 = voltage_timeseries(blox[1], sol)\nv2 = voltage_timeseries(blox[2], sol)\n\nfig = Figure()\nax = Axis(fig[1,1]; xlabel = \"time (ms)\", ylabel = \"Potential\")\nlines!(ax, sol.t, v1)\nlines!(ax, sol.t, v2)\nxlims!(ax, (0, 1000)) ## limit the x-axis to the first second of simulation\nfig","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"To evaluate the connectivity of our simulated resting state network, we calculate the statistically significant correlations","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"step_sz = 1000\ntime_steps = range(1, length(sol.t); step = step_sz)\n\ncs = Array{Float64}(undef, N_bloxs, N_bloxs, length(time_steps)-1)\n\n# First we get the voltage timeseries of all blox in time bins\nfor (i, t) in enumerate(time_steps[1:end-1])\n # Get the voltage timeseries of all blox within a time window in a matrix\n V = voltage_timeseries(blox, sol; ts = t:(t + step_sz))\n # Calculate the correlation matrix of all timeseries per time window\n cs[:,:,i] = cor(V)\nend\n\np = zeros(N_bloxs, N_bloxs)\nfor i in 1:N_bloxs\n for j in 1:N_bloxs\n # Perform a t-test on the voltage correlations across all time windows\n p[i,j] = pvalue(OneSampleTTest(cs[i,j,:]))\n end\nend\n\nfig = Figure()\nax = [\n Axis(fig[1,1]; title = \"Correlation p-values\", aspect = AxisAspect(1)),\n Axis(fig[1,2]; title = \"Adjacency matrix\", aspect = AxisAspect(1))\n]\n# Use logical indexing to only plot the statistically significan p-values (p .< 0.05)\nheatmap!(ax[1], log10.(p) .* (p .< 0.05))\n\nheatmap!(ax[2], wm)\nfig","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"Notice how the correlation heatmap qualitatively matches the sparsity structure that we printed above with wm_sparse.","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"","category":"page"},{"location":"tutorials/resting_state/","page":"Resting state simulation using neural mass models","title":"Resting state simulation using neural mass models","text":"This page was generated using Literate.jl.","category":"page"}] } diff --git a/dev/tutorials/basal_ganglia/index.html b/dev/tutorials/basal_ganglia/index.html index 8a48a2c..7a6de5e 100644 --- a/dev/tutorials/basal_ganglia/index.html +++ b/dev/tutorials/basal_ganglia/index.html @@ -168,4 +168,4 @@ title = "STN (PD)") resize!(fig.scene, (1600, 900)) -figExample block output

We see the emergence of strong beta oscillations in the Parkinsonian condition compared to the baseline condition for all neural populations. This aligns with the findings of Adam et al. and reflects the pathological synchrony observed in Parkinson's disease.

References

Adam, Elie M., et al. "Deep brain stimulation in the subthalamic nucleus for Parkinson's disease can restore dynamics of striatal networks." Proceedings of the National Academy of Sciences 119.19 (2022): e2120808119.


This page was generated using Literate.jl.

+figExample block output

We see the emergence of strong beta oscillations in the Parkinsonian condition compared to the baseline condition for all neural populations. This aligns with the findings of Adam et al. and reflects the pathological synchrony observed in Parkinson's disease.

References

Adam, Elie M., et al. "Deep brain stimulation in the subthalamic nucleus for Parkinson's disease can restore dynamics of striatal networks." Proceedings of the National Academy of Sciences 119.19 (2022): e2120808119.


This page was generated using Literate.jl.

diff --git a/dev/tutorials/neural_assembly/index.html b/dev/tutorials/neural_assembly/index.html index 4afa8ef..613eea6 100644 --- a/dev/tutorials/neural_assembly/index.html +++ b/dev/tutorials/neural_assembly/index.html @@ -182,4 +182,4 @@ fig = Figure(); ax = Axis(fig[1,1]; xlabel = "time (ms)", ylabel = "Voltage (mv)") lines!(ax, sol.t, mnv) -fig ## to display the figureExample block output

and finally the AC powerspectrum

powerspectrumplot(AC, sol)
Example block output

Sugestion : Try changing the image samples and notice the change in the spatial firing patterns in VAC and AC neurons. One can make multiple cortical blocks simillar to AC and connect them in various connection topologies. All of them can directly or indirectly get input from VAC.

References

[1] Pathak A., Brincat S., Organtzidis H., Strey H., Senneff S., Antzoulatos E., Mujica-Parodi L., Miller E., Granger R. Biomimetic model of corticostriatal micro-assemblies discovers new neural code., bioRxiv 2023.11.06.565902, 2024


This page was generated using Literate.jl.

+fig ## to display the figureExample block output

and finally the AC powerspectrum

powerspectrumplot(AC, sol)
Example block output

Sugestion : Try changing the image samples and notice the change in the spatial firing patterns in VAC and AC neurons. One can make multiple cortical blocks simillar to AC and connect them in various connection topologies. All of them can directly or indirectly get input from VAC.

References

[1] Pathak A., Brincat S., Organtzidis H., Strey H., Senneff S., Antzoulatos E., Mujica-Parodi L., Miller E., Granger R. Biomimetic model of corticostriatal micro-assemblies discovers new neural code., bioRxiv 2023.11.06.565902, 2024


This page was generated using Literate.jl.

diff --git a/dev/tutorials/parkinsons/index.html b/dev/tutorials/parkinsons/index.html index 2610e8a..21c97a4 100644 --- a/dev/tutorials/parkinsons/index.html +++ b/dev/tutorials/parkinsons/index.html @@ -66,4 +66,4 @@ prob = remake(prob, tspan = (0.0, sim_dur)) ## Remake the ODEProblem with the new simulation time sol = solve(prob, Tsit5(), saveat=0.1); ## Solve the new problem

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.

powerspectrumplot(D1, sol, state = "y", ## specify the block to plot, the solution object, and the state of the block to plot
                   method = welch_pgram, window = hanning, ## power spectrum estimation method
-                  title="D1 Population Power Spectrum")
Example block output

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.

Further Exploration

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 $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. 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! If you're inspired to continue experimenting, here are a couple other ideas:

References

[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 [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.


This page was generated using Literate.jl.

+ title="D1 Population Power Spectrum")Example block output

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.

Further Exploration

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 $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. 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! If you're inspired to continue experimenting, here are a couple other ideas:

References

[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 [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.


This page was generated using Literate.jl.

diff --git a/dev/tutorials/ping_network/index.html b/dev/tutorials/ping_network/index.html index 6ffbd88..81c9489 100644 --- a/dev/tutorials/ping_network/index.html +++ b/dev/tutorials/ping_network/index.html @@ -46,4 +46,4 @@ sol = solve(prob, Tsit5(), saveat=0.1); ## Solve the problem and save at 0.1ms resolution.

Plotting the results

Now that we have a whole simulation, let's plot the results and see how they line up with the original figures. We're looking to reproduce the dynamics shown in Figure 1 of Börgers et al. [1]. To create raster plots in Neuroblox for the excitatory and inhibitory populations, it is as simple as:

fig = Figure()
 rasterplot(fig[1,1], exci, sol; threshold=20.0, title="Excitatory Neurons")
 rasterplot(fig[2,1], inhib, sol; threshold=20.0, title="Inhibitory Neurons")
-fig
Example block output

The upper panel should show the dynamics in Figure 1.C, with a clear population of excitatory neurons firing together from the external driving current, and the other excitatory neurons exhibiting more stochastic bursts. The lower panel should show the dynamics in Figure 1.A, with the inhibitory neurons firing in a more synchronous manner than the excitatory neurons.

Conclusion

And there you have it! A complete PING demonstration that reproduces the dynamics of a published paper in a matter of 30 seconds, give or take. Have fun making your own!

For further exploration

If you want to explore the PING network further, you can try the following:

  1. You might notice that the excitatory and inhibitory populations become slightly desynchronized by the end of the simulation, unlike in the original paper. This is because of slight differences in how we implement the excitatory drive and inhibitory bath, which adjusts the overall E/I balance. Try increasing the inhibitory bath or decreasing the percentage of excitatory neurons that receive input and see how this affects the synchrony!
  2. This tutorial is written entirely using built-in blocks for the excitatory and inhibitory neurons. If you want to explore the details, try typing $?PINGNeuronExci$ or $?PINGNeuronInhib$ in your Julia REPL to see the full details of the blocks. If you really want to dig into the details, type $@edit PINGNeuronExci()$ to open the source code and see how the equations are written.

References

  1. Börgers C, Epstein S, Kopell NJ. Gamma oscillations mediate stimulus competition and attentional selection in a cortical network model. Proc Natl Acad Sci U S A. 2008 Nov 18;105(46):18023-8. DOI: 10.1073/pnas.0809511105.
  2. Traub, RD, Miles, R. Neuronal Networks of the Hippocampus. Cambridge University Press, Cambridge, UK, 1991. DOI: 10.1017/CBO9780511895401
  3. Wang, X-J, Buzsáki, G. Gamma oscillation by synaptic inhibition in a hippocampal interneuronal network model. J. Neurosci., 16:6402–6413, 1996. DOI: 10.1523/JNEUROSCI.16-20-06402.1996

This page was generated using Literate.jl.

+figExample block output

The upper panel should show the dynamics in Figure 1.C, with a clear population of excitatory neurons firing together from the external driving current, and the other excitatory neurons exhibiting more stochastic bursts. The lower panel should show the dynamics in Figure 1.A, with the inhibitory neurons firing in a more synchronous manner than the excitatory neurons.

Conclusion

And there you have it! A complete PING demonstration that reproduces the dynamics of a published paper in a matter of 30 seconds, give or take. Have fun making your own!

For further exploration

If you want to explore the PING network further, you can try the following:

  1. You might notice that the excitatory and inhibitory populations become slightly desynchronized by the end of the simulation, unlike in the original paper. This is because of slight differences in how we implement the excitatory drive and inhibitory bath, which adjusts the overall E/I balance. Try increasing the inhibitory bath or decreasing the percentage of excitatory neurons that receive input and see how this affects the synchrony!
  2. This tutorial is written entirely using built-in blocks for the excitatory and inhibitory neurons. If you want to explore the details, try typing $?PINGNeuronExci$ or $?PINGNeuronInhib$ in your Julia REPL to see the full details of the blocks. If you really want to dig into the details, type $@edit PINGNeuronExci()$ to open the source code and see how the equations are written.

References

  1. Börgers C, Epstein S, Kopell NJ. Gamma oscillations mediate stimulus competition and attentional selection in a cortical network model. Proc Natl Acad Sci U S A. 2008 Nov 18;105(46):18023-8. DOI: 10.1073/pnas.0809511105.
  2. Traub, RD, Miles, R. Neuronal Networks of the Hippocampus. Cambridge University Press, Cambridge, UK, 1991. DOI: 10.1017/CBO9780511895401
  3. Wang, X-J, Buzsáki, G. Gamma oscillation by synaptic inhibition in a hippocampal interneuronal network model. J. Neurosci., 16:6402–6413, 1996. DOI: 10.1523/JNEUROSCI.16-20-06402.1996

This page was generated using Literate.jl.

diff --git a/dev/tutorials/resting_state/index.html b/dev/tutorials/resting_state/index.html index 8109aed..80761b4 100644 --- a/dev/tutorials/resting_state/index.html +++ b/dev/tutorials/resting_state/index.html @@ -87,4 +87,4 @@ heatmap!(ax[1], log10.(p) .* (p .< 0.05)) heatmap!(ax[2], wm) -figExample block output

Notice how the correlation heatmap qualitatively matches the sparsity structure that we printed above with wm_sparse.


This page was generated using Literate.jl.

+figExample block output

Notice how the correlation heatmap qualitatively matches the sparsity structure that we printed above with wm_sparse.


This page was generated using Literate.jl.

diff --git a/dev/tutorials/spectralDCM.jl b/dev/tutorials/spectralDCM.jl index edfa48f..aa5aee3 100644 --- a/dev/tutorials/spectralDCM.jl +++ b/dev/tutorials/spectralDCM.jl @@ -51,11 +51,11 @@ for i = 1:nr ## add Ornstein-Uhlenbeck block as noisy input to the current region input = OUBlox(;name=Symbol("r$(i)₊ou"), σ=0.1) - add_edge!(g, input => region; :weight => 1/16) # Note that 1/16 is taken from SPM12, this stabilizes the balloon model simulation. Alternatively the noise of the Ornstein-Uhlenbeck block or the weight of the edge connecting neuronal activity and balloon model could be reduced to guarantee numerical stability. + add_edge!(g, input => region, weight=1/16) # Note that 1/16 is taken from SPM12, this stabilizes the balloon model simulation. Alternatively the noise of the Ornstein-Uhlenbeck block or the weight of the edge connecting neuronal activity and balloon model could be reduced to guarantee numerical stability. ## simulate fMRI signal with BalloonModel which includes the BOLD signal on top of the balloon model dynamics measurement = BalloonModel(;name=Symbol("r$(i)₊bm")) - add_edge!(g, region => measurement; :weight => 1.0) + add_edge!(g, region => measurement, weight=1.0) end # Next we define the between-region connectivity matrix and make sure that it is diagonally dominant to guarantee numerical stability (see Gershgorin theorem). A_true = 0.1*randn(nr, nr) @@ -63,7 +63,7 @@ A_true -= diagm(map(a -> sum(abs, a), eachrow(A_true))) # ensure diagonal dom # Instead of a random matrix use the same matrix as is defined in [3] A_true = [[-0.5 -2 0]; [0.4 -0.5 -0.3]; [0 0.2 -0.5]] for idx in CartesianIndices(A_true) - add_edge!(g, regions[idx[1]] => regions[idx[2]]; :weight => A_true[idx[1], idx[2]]) + add_edge!(g, regions[idx[1]] => regions[idx[2]], weight=A_true[idx[1], idx[2]]) end # finally we compose the simulation model @@ -72,7 +72,7 @@ end # ## Run the simulation and plot the results # setup simulation of the model, time in seconds -tspan = (0.0, 612.0) +tspan = (0.0, 512.0) prob = SDEProblem(simmodel, [], tspan) dt = 2.0 # two seconds as measurement interval for fMRI sol = solve(prob, ImplicitRKMil(), saveat=dt); @@ -125,11 +125,11 @@ for i = 1:nr region = LinearNeuralMass(;name=Symbol("r$(i)₊lm")) push!(regions, region) input = ExternalInput(;name=Symbol("r$(i)₊ei")) - add_edge!(g, input => region; :weight => C) + add_edge!(g, input => region, weight=C) ## we assume fMRI signal and model them with a BalloonModel measurement = BalloonModel(;name=Symbol("r$(i)₊bm"), lnτ=lnτ, lnκ=lnκ, lnϵ=lnϵ) - add_edge!(g, region => measurement; :weight => 1.0) + add_edge!(g, region => measurement, weight=1.0) end A_prior = 0.01*randn(nr, nr) @@ -141,18 +141,21 @@ for (i, a) in enumerate(vec(A_prior)) symb = Symbol("A$(i)") push!(A, only(@parameters $symb = a)) end -# With the function `untune!`` we can list indices of parameters whose tunable flag should be set to false. -# For instance the first element in the second row: -untune!(A, []) + for (i, idx) in enumerate(CartesianIndices(A_prior)) if idx[1] == idx[2] - add_edge!(g, regions[idx[1]] => regions[idx[2]]; :weight => -exp(A[i])/2) # -exp(A[i])/2: treatement of diagonal elements in SPM12 to make diagonal dominance (see Gershgorin Theorem) more likely but it is not guaranteed + add_edge!(g, regions[idx[1]] => regions[idx[2]], weight=-exp(A[i])/2) # -exp(A[i])/2: treatement of diagonal elements in SPM12 to make diagonal dominance (see Gershgorin Theorem) more likely but it is not guaranteed else - add_edge!(g, regions[idx[2]] => regions[idx[1]]; :weight => A[i]) + add_edge!(g, regions[idx[2]] => regions[idx[1]], weight=A[i]) end end - -@named fitmodel = system_from_graph(g, split=false) +# we avoid simplification of the model in order to exclude some parameters from fitting +@named fitmodel = system_from_graph(g, simplify=false) +# With the function `changetune`` we can provide a dictionary of parameters whose tunable flag should be changed, for instance set to false to exclude them from the optimizatoin procedure. +# For instance the the effective connections that are set to zero in the simulation: +untune = Dict(A[3] => false, A[7] => false) +fitmodel = changetune(fitmodel, untune) # 3 and 7 are not present in the simulation model +fitmodel = structural_simplify(fitmodel, split=false) # and now simplify the euqations # ## Setup spectral DCM max_iter = 128; # maximum number of iterations diff --git a/dev/tutorials/spectralDCM/0996edce.png b/dev/tutorials/spectralDCM/0996edce.png new file mode 100644 index 0000000..a01ebd4 Binary files /dev/null and b/dev/tutorials/spectralDCM/0996edce.png differ diff --git a/dev/tutorials/spectralDCM/477d7083.png b/dev/tutorials/spectralDCM/477d7083.png new file mode 100644 index 0000000..20d0fe6 Binary files /dev/null and b/dev/tutorials/spectralDCM/477d7083.png differ diff --git a/dev/tutorials/spectralDCM/58d40227.png b/dev/tutorials/spectralDCM/58d40227.png new file mode 100644 index 0000000..70e4932 Binary files /dev/null and b/dev/tutorials/spectralDCM/58d40227.png differ diff --git a/dev/tutorials/spectralDCM/8bc5671f.png b/dev/tutorials/spectralDCM/8bc5671f.png new file mode 100644 index 0000000..667c808 Binary files /dev/null and b/dev/tutorials/spectralDCM/8bc5671f.png differ diff --git a/dev/tutorials/spectralDCM/abf7f725.png b/dev/tutorials/spectralDCM/abf7f725.png deleted file mode 100644 index 145defe..0000000 Binary files a/dev/tutorials/spectralDCM/abf7f725.png and /dev/null differ diff --git a/dev/tutorials/spectralDCM/b8b10f4f.png b/dev/tutorials/spectralDCM/b8b10f4f.png deleted file mode 100644 index f179aea..0000000 Binary files a/dev/tutorials/spectralDCM/b8b10f4f.png and /dev/null differ diff --git a/dev/tutorials/spectralDCM/cfedabb7.png b/dev/tutorials/spectralDCM/cfedabb7.png deleted file mode 100644 index 4012fa2..0000000 Binary files a/dev/tutorials/spectralDCM/cfedabb7.png and /dev/null differ diff --git a/dev/tutorials/spectralDCM/d8e5c62a.png b/dev/tutorials/spectralDCM/d8e5c62a.png deleted file mode 100644 index ef1cec2..0000000 Binary files a/dev/tutorials/spectralDCM/d8e5c62a.png and /dev/null differ diff --git a/dev/tutorials/spectralDCM/index.html b/dev/tutorials/spectralDCM/index.html index 2af526c..378002d 100644 --- a/dev/tutorials/spectralDCM/index.html +++ b/dev/tutorials/spectralDCM/index.html @@ -13,18 +13,18 @@ # add Ornstein-Uhlenbeck block as noisy input to the current region input = OUBlox(;name=Symbol("r$(i)₊ou"), σ=0.1) - add_edge!(g, input => region; :weight => 1/16) # Note that 1/16 is taken from SPM12, this stabilizes the balloon model simulation. Alternatively the noise of the Ornstein-Uhlenbeck block or the weight of the edge connecting neuronal activity and balloon model could be reduced to guarantee numerical stability. + add_edge!(g, input => region, weight=1/16) # Note that 1/16 is taken from SPM12, this stabilizes the balloon model simulation. Alternatively the noise of the Ornstein-Uhlenbeck block or the weight of the edge connecting neuronal activity and balloon model could be reduced to guarantee numerical stability. # simulate fMRI signal with BalloonModel which includes the BOLD signal on top of the balloon model dynamics measurement = BalloonModel(;name=Symbol("r$(i)₊bm")) - add_edge!(g, region => measurement; :weight => 1.0) + add_edge!(g, region => measurement, weight=1.0) end

Next we define the between-region connectivity matrix and make sure that it is diagonally dominant to guarantee numerical stability (see Gershgorin theorem).

A_true = 0.1*randn(nr, nr)
 A_true -= diagm(map(a -> sum(abs, a), eachrow(A_true)))    # ensure diagonal dominance of matrix
3×3 Matrix{Float64}:
  -0.220731  -0.123353   0.0612029
  -0.121674  -0.140556  -0.0188823
  -0.177178   0.179645  -0.660217

Instead of a random matrix use the same matrix as is defined in [3]

A_true = [[-0.5 -2 0]; [0.4 -0.5 -0.3]; [0 0.2 -0.5]]
 for idx in CartesianIndices(A_true)
-    add_edge!(g, regions[idx[1]] => regions[idx[2]]; :weight => A_true[idx[1], idx[2]])
+    add_edge!(g, regions[idx[1]] => regions[idx[2]], weight=A_true[idx[1], idx[2]])
 end

finally we compose the simulation model

@named simmodel = system_from_graph(g, split=false)

\[ \begin{align} \frac{\mathrm{d} \mathtt{r1.ou.x}\left( t \right)}{\mathrm{d}t} &= \frac{\mathtt{r1.ou.\mu} - \mathtt{r1.ou.x}\left( t \right)}{\mathtt{r1.ou.\tau}} \\ \frac{\mathrm{d} \mathtt{r1.lm.x}\left( t \right)}{\mathrm{d}t} &= \mathtt{w\_r1.lm\_r1.lm} \mathtt{r1.lm.x}\left( t \right) + \mathtt{w\_r1.ou\_r1.lm} \mathtt{r1.ou.x}\left( t \right) + \mathtt{w\_r2.lm\_r1.lm} \mathtt{r2.lm.x}\left( t \right) + \mathtt{w\_r3.lm\_r1.lm} \mathtt{r3.lm.x}\left( t \right) \\ @@ -48,7 +48,7 @@ 0 &= - \mathtt{r2.bm.bold}\left( t \right) + 4 \left( 3.7726 - 0.6 e^{\mathtt{r2.bm.ln\epsilon}} - 2.7726 e^{\mathtt{r2.bm.lnq}\left( t \right)} + \frac{ - 0.4 e^{\mathtt{r2.bm.ln\epsilon}} e^{\mathtt{r2.bm.lnq}\left( t \right)}}{e^{\mathtt{r2.bm.ln\nu}\left( t \right)}} + \left( -1 + e^{\mathtt{r2.bm.ln\epsilon}} \right) e^{\mathtt{r2.bm.ln\nu}\left( t \right)} \right) \\ 0 &= - \mathtt{r3.bm.bold}\left( t \right) + 4 \left( 3.7726 - 0.6 e^{\mathtt{r3.bm.ln\epsilon}} - 2.7726 e^{\mathtt{r3.bm.lnq}\left( t \right)} + \frac{ - 0.4 e^{\mathtt{r3.bm.ln\epsilon}} e^{\mathtt{r3.bm.lnq}\left( t \right)}}{e^{\mathtt{r3.bm.ln\nu}\left( t \right)}} + e^{\mathtt{r3.bm.ln\nu}\left( t \right)} \left( -1 + e^{\mathtt{r3.bm.ln\epsilon}} \right) \right) \end{align} - \]

Run the simulation and plot the results

setup simulation of the model, time in seconds

tspan = (0.0, 612.0)
+ \]

Run the simulation and plot the results

setup simulation of the model, time in seconds

tspan = (0.0, 512.0)
 prob = SDEProblem(simmodel, [], tspan)
 dt = 2.0   # two seconds as measurement interval for fMRI
 sol = solve(prob, ImplicitRKMil(), saveat=dt);

plot bold signal time series

idx_m = get_idx_tagged_vars(simmodel, "measurement")    # get index of bold signal
@@ -59,7 +59,7 @@
     ylabel = "BOLD",
 )
 lines!(ax, sol, idxs=idx_m)
-f
Example block output

We note that the initial spike is not meaningful and a result of the equilibration of the stochastic process thus we remove it.

dfsol = DataFrame(sol[ceil(Int, 101/dt):end]);

Estimate and plot the cross-spectral densities

data = Matrix(dfsol[:, idx_m]);

We compute the cross-spectral density by fitting a linear model of order p and then compute the csd analytically from the parameters of the multivariate autoregressive model

p = 8
+f
Example block output

We note that the initial spike is not meaningful and a result of the equilibration of the stochastic process thus we remove it.

dfsol = DataFrame(sol[ceil(Int, 101/dt):end]);

Estimate and plot the cross-spectral densities

data = Matrix(dfsol[:, idx_m]);

We compute the cross-spectral density by fitting a linear model of order p and then compute the csd analytically from the parameters of the multivariate autoregressive model

p = 8
 mar = mar_ml(data, p)   # maximum likelihood estimation of the MAR coefficients and noise covariance matrix
 ns = size(data, 1)
 freq = range(min(128, ns*dt)^-1, max(8, 2*dt)^-1, 32)
@@ -71,7 +71,7 @@
         lines!(ax, freq, real.(csd[:, i, j]))
     end
 end
-fig
Example block output

Model Inference

We will now assemble a new model that is used for fitting the previous simulations. This procedure is similar to before with the difference that we will define global parameters and use tags such as [tunable=false/true] to define which parameters we will want to estimate. Note that parameters are tunable by default.

g = MetaDiGraph()
+fig
Example block output

Model Inference

We will now assemble a new model that is used for fitting the previous simulations. This procedure is similar to before with the difference that we will define global parameters and use tags such as [tunable=false/true] to define which parameters we will want to estimate. Note that parameters are tunable by default.

g = MetaDiGraph()
 regions = [];   # list of neural mass blocks to then connect them to each other with an adjacency matrix `A`

The following parameters are shared accross regions, which is why we define them here.

@parameters lnκ=0.0 [tunable=false] lnϵ=0.0 [tunable=false] lnτ=0.0 [tunable=false]   # lnκ: decay parameter for hemodynamics; lnϵ: ratio of intra- to extra-vascular components, lnτ: transit time scale
 @parameters C=1/16 [tunable=false]   # note that C=1/16 is taken from SPM12 and stabilizes the balloon model simulation. See also comment above.
 
@@ -79,11 +79,11 @@
     region = LinearNeuralMass(;name=Symbol("r$(i)₊lm"))
     push!(regions, region)
     input = ExternalInput(;name=Symbol("r$(i)₊ei"))
-    add_edge!(g, input => region; :weight => C)
+    add_edge!(g, input => region, weight=C)
 
     # we assume fMRI signal and model them with a BalloonModel
     measurement = BalloonModel(;name=Symbol("r$(i)₊bm"), lnτ=lnτ, lnκ=lnκ, lnϵ=lnϵ)
-    add_edge!(g, region => measurement; :weight => 1.0)
+    add_edge!(g, region => measurement, weight=1.0)
 end
 
 A_prior = 0.01*randn(nr, nr)
@@ -94,16 +94,46 @@
 for (i, a) in enumerate(vec(A_prior))
     symb = Symbol("A$(i)")
     push!(A, only(@parameters $symb = a))
-end

With the function untune!` we can list indices of parameters whose tunable flag should be set to false. For instance the first element in the second row:

untune!(A, [])
+end
+
 for (i, idx) in enumerate(CartesianIndices(A_prior))
     if idx[1] == idx[2]
-        add_edge!(g, regions[idx[1]] => regions[idx[2]]; :weight => -exp(A[i])/2)  # -exp(A[i])/2: treatement of diagonal elements in SPM12 to make diagonal dominance (see Gershgorin Theorem) more likely but it is not guaranteed
+        add_edge!(g, regions[idx[1]] => regions[idx[2]], weight=-exp(A[i])/2)  # -exp(A[i])/2: treatement of diagonal elements in SPM12 to make diagonal dominance (see Gershgorin Theorem) more likely but it is not guaranteed
     else
-        add_edge!(g, regions[idx[2]] => regions[idx[1]]; :weight => A[i])
+        add_edge!(g, regions[idx[2]] => regions[idx[1]], weight=A[i])
     end
-end
-
-@named fitmodel = system_from_graph(g, split=false)

\[ \begin{align} +end

we avoid simplification of the model in order to exclude some parameters from fitting

@named fitmodel = system_from_graph(g, simplify=false)

\[ \begin{align} +\mathtt{r1.lm.jcn}\left( t \right) &= \mathtt{A4} \mathtt{r2.lm.x}\left( t \right) + \mathtt{A7} \mathtt{r3.lm.x}\left( t \right) + C \mathtt{r1.ei.u}\left( t \right) - \frac{1}{2} e^{\mathtt{A1}} \mathtt{r1.lm.x}\left( t \right) \\ +\mathtt{r1.bm.jcn}\left( t \right) &= \mathtt{w\_r1.lm\_r1.bm} \mathtt{r1.lm.x}\left( t \right) \\ +\mathtt{r2.lm.jcn}\left( t \right) &= \mathtt{A2} \mathtt{r1.lm.x}\left( t \right) + \mathtt{A8} \mathtt{r3.lm.x}\left( t \right) + C \mathtt{r2.ei.u}\left( t \right) - \frac{1}{2} \mathtt{r2.lm.x}\left( t \right) e^{\mathtt{A5}} \\ +\mathtt{r2.bm.jcn}\left( t \right) &= \mathtt{w\_r2.lm\_r2.bm} \mathtt{r2.lm.x}\left( t \right) \\ +\mathtt{r3.lm.jcn}\left( t \right) &= \mathtt{A3} \mathtt{r1.lm.x}\left( t \right) + \mathtt{A6} \mathtt{r2.lm.x}\left( t \right) + C \mathtt{r3.ei.u}\left( t \right) - \frac{1}{2} e^{\mathtt{A9}} \mathtt{r3.lm.x}\left( t \right) \\ +\mathtt{r3.bm.jcn}\left( t \right) &= \mathtt{w\_r3.lm\_r3.bm} \mathtt{r3.lm.x}\left( t \right) \\ +\mathtt{r1.ei.u}\left( t \right) &= 1 \\ +\frac{\mathrm{d} \mathtt{r1.lm.x}\left( t \right)}{\mathrm{d}t} &= \mathtt{r1.lm.jcn}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{r1.bm.s}\left( t \right)}{\mathrm{d}t} &= \mathtt{r1.bm.jcn}\left( t \right) - 0.32 \left( -1 + e^{\mathtt{r1.bm.lnu}\left( t \right)} \right) - 0.64 e^{\mathtt{ln\kappa}} \mathtt{r1.bm.s}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{r1.bm.lnu}\left( t \right)}{\mathrm{d}t} &= \frac{\mathtt{r1.bm.s}\left( t \right)}{e^{\mathtt{r1.bm.lnu}\left( t \right)}} \\ +\frac{\mathrm{d} \mathtt{r1.bm.ln\nu}\left( t \right)}{\mathrm{d}t} &= \frac{e^{\mathtt{r1.bm.lnu}\left( t \right)} - \left( e^{\mathtt{r1.bm.ln\nu}\left( t \right)} \right)^{3.125}}{2 e^{\mathtt{ln\tau}} e^{\mathtt{r1.bm.ln\nu}\left( t \right)}} \\ +\frac{\mathrm{d} \mathtt{r1.bm.lnq}\left( t \right)}{\mathrm{d}t} &= \frac{\frac{2.5 e^{\mathtt{r1.bm.lnu}\left( t \right)} \left( 1 - 0.6^{\frac{1}{e^{\mathtt{r1.bm.lnu}\left( t \right)}}} \right)}{e^{\mathtt{r1.bm.lnq}\left( t \right)}} - \left( e^{\mathtt{r1.bm.ln\nu}\left( t \right)} \right)^{2.125}}{2 e^{\mathtt{ln\tau}}} \\ +\mathtt{r1.bm.bold}\left( t \right) &= 4 \left( 3.7726 + \frac{ - 0.4 e^{\mathtt{ln\epsilon}} e^{\mathtt{r1.bm.lnq}\left( t \right)}}{e^{\mathtt{r1.bm.ln\nu}\left( t \right)}} - 0.6 e^{\mathtt{ln\epsilon}} - 2.7726 e^{\mathtt{r1.bm.lnq}\left( t \right)} + \left( -1 + e^{\mathtt{ln\epsilon}} \right) e^{\mathtt{r1.bm.ln\nu}\left( t \right)} \right) \\ +\mathtt{r2.ei.u}\left( t \right) &= 1 \\ +\frac{\mathrm{d} \mathtt{r2.lm.x}\left( t \right)}{\mathrm{d}t} &= \mathtt{r2.lm.jcn}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{r2.bm.s}\left( t \right)}{\mathrm{d}t} &= - 0.32 \left( -1 + e^{\mathtt{r2.bm.lnu}\left( t \right)} \right) + \mathtt{r2.bm.jcn}\left( t \right) - 0.64 e^{\mathtt{ln\kappa}} \mathtt{r2.bm.s}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{r2.bm.lnu}\left( t \right)}{\mathrm{d}t} &= \frac{\mathtt{r2.bm.s}\left( t \right)}{e^{\mathtt{r2.bm.lnu}\left( t \right)}} \\ +\frac{\mathrm{d} \mathtt{r2.bm.ln\nu}\left( t \right)}{\mathrm{d}t} &= \frac{e^{\mathtt{r2.bm.lnu}\left( t \right)} - \left( e^{\mathtt{r2.bm.ln\nu}\left( t \right)} \right)^{3.125}}{2 e^{\mathtt{ln\tau}} e^{\mathtt{r2.bm.ln\nu}\left( t \right)}} \\ +\frac{\mathrm{d} \mathtt{r2.bm.lnq}\left( t \right)}{\mathrm{d}t} &= \frac{\frac{2.5 \left( 1 - 0.6^{\frac{1}{e^{\mathtt{r2.bm.lnu}\left( t \right)}}} \right) e^{\mathtt{r2.bm.lnu}\left( t \right)}}{e^{\mathtt{r2.bm.lnq}\left( t \right)}} - \left( e^{\mathtt{r2.bm.ln\nu}\left( t \right)} \right)^{2.125}}{2 e^{\mathtt{ln\tau}}} \\ +\mathtt{r2.bm.bold}\left( t \right) &= 4 \left( 3.7726 + \frac{ - 0.4 e^{\mathtt{r2.bm.lnq}\left( t \right)} e^{\mathtt{ln\epsilon}}}{e^{\mathtt{r2.bm.ln\nu}\left( t \right)}} - 2.7726 e^{\mathtt{r2.bm.lnq}\left( t \right)} - 0.6 e^{\mathtt{ln\epsilon}} + e^{\mathtt{r2.bm.ln\nu}\left( t \right)} \left( -1 + e^{\mathtt{ln\epsilon}} \right) \right) \\ +\mathtt{r3.ei.u}\left( t \right) &= 1 \\ +\frac{\mathrm{d} \mathtt{r3.lm.x}\left( t \right)}{\mathrm{d}t} &= \mathtt{r3.lm.jcn}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{r3.bm.s}\left( t \right)}{\mathrm{d}t} &= - 0.32 \left( -1 + e^{\mathtt{r3.bm.lnu}\left( t \right)} \right) + \mathtt{r3.bm.jcn}\left( t \right) - 0.64 e^{\mathtt{ln\kappa}} \mathtt{r3.bm.s}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{r3.bm.lnu}\left( t \right)}{\mathrm{d}t} &= \frac{\mathtt{r3.bm.s}\left( t \right)}{e^{\mathtt{r3.bm.lnu}\left( t \right)}} \\ +\frac{\mathrm{d} \mathtt{r3.bm.ln\nu}\left( t \right)}{\mathrm{d}t} &= \frac{e^{\mathtt{r3.bm.lnu}\left( t \right)} - \left( e^{\mathtt{r3.bm.ln\nu}\left( t \right)} \right)^{3.125}}{2 e^{\mathtt{ln\tau}} e^{\mathtt{r3.bm.ln\nu}\left( t \right)}} \\ +\frac{\mathrm{d} \mathtt{r3.bm.lnq}\left( t \right)}{\mathrm{d}t} &= \frac{\frac{2.5 \left( 1 - 0.6^{\frac{1}{e^{\mathtt{r3.bm.lnu}\left( t \right)}}} \right) e^{\mathtt{r3.bm.lnu}\left( t \right)}}{e^{\mathtt{r3.bm.lnq}\left( t \right)}} - \left( e^{\mathtt{r3.bm.ln\nu}\left( t \right)} \right)^{2.125}}{2 e^{\mathtt{ln\tau}}} \\ +\mathtt{r3.bm.bold}\left( t \right) &= 4 \left( 3.7726 - 2.7726 e^{\mathtt{r3.bm.lnq}\left( t \right)} - 0.6 e^{\mathtt{ln\epsilon}} + \frac{ - 0.4 e^{\mathtt{r3.bm.lnq}\left( t \right)} e^{\mathtt{ln\epsilon}}}{e^{\mathtt{r3.bm.ln\nu}\left( t \right)}} + e^{\mathtt{r3.bm.ln\nu}\left( t \right)} \left( -1 + e^{\mathtt{ln\epsilon}} \right) \right) +\end{align} + \]

With the function changetune` we can provide a dictionary of parameters whose tunable flag should be changed, for instance set to false to exclude them from the optimizatoin procedure. For instance the the effective connections that are set to zero in the simulation:

untune = Dict(A[3] => false, A[7] => false)
+fitmodel = changetune(fitmodel, untune)                 # 3 and 7 are not present in the simulation model
+fitmodel = structural_simplify(fitmodel, split=false)   # and now simplify the euqations

\[ \begin{align} \frac{\mathrm{d} \mathtt{r1.lm.x}\left( t \right)}{\mathrm{d}t} &= \mathtt{r1.lm.jcn}\left( t \right) \\ \frac{\mathrm{d} \mathtt{r1.bm.s}\left( t \right)}{\mathrm{d}t} &= \mathtt{r1.bm.jcn}\left( t \right) - 0.32 \left( -1 + e^{\mathtt{r1.bm.lnu}\left( t \right)} \right) - 0.64 e^{\mathtt{ln\kappa}} \mathtt{r1.bm.s}\left( t \right) \\ \frac{\mathrm{d} \mathtt{r1.bm.lnu}\left( t \right)}{\mathrm{d}t} &= \frac{\mathtt{r1.bm.s}\left( t \right)}{e^{\mathtt{r1.bm.lnu}\left( t \right)}} \\ @@ -168,24 +198,21 @@ end end end -end

iteration: 1 - F:0.0 - dF predicted:537.9666422884662
-iteration: 2 - F:390.8593747753821 - dF predicted:892.8426748351383
-iteration: 3 - F:798.8442268171907 - dF predicted:221.1688671552351
-iteration: 4 - F:1370.9053560070888 - dF predicted:839.2878877974613
-iteration: 5 - F:1885.6717314773691 - dF predicted:361.1388458803659
-iteration: 6 - F:2259.290133226981 - dF predicted:188.95248271474514
-iteration: 7 - F:2653.5387554658414 - dF predicted:513.5601528042832
-iteration: 8 - F:2825.1096408030808 - dF predicted:52.69418526444423
-iteration: 9 - F:2858.6334655903165 - dF predicted:17.656027334122456
-iteration: 10 - F:2921.0685157150224 - dF predicted:12.013021315467913
-iteration: 11 - F:2929.1611627539337 - dF predicted:8.524204672739927
-iteration: 12 - F:2934.355074788426 - dF predicted:6.285594503102924
-iteration: 13 - F:2937.8691881640857 - dF predicted:4.116711612885558
-iteration: 14 - F:2939.9829735371277 - dF predicted:3.422951613340519
-iteration: 15 - F:2940.2583440059625 - dF predicted:6.17886421579837
-iteration: 16 - F:2940.2583440059625 - dF predicted:4.2184118073144585
-iteration: 17 - F:2941.9501812739672 - dF predicted:0.02475546957417761
-iteration: 18 - F:2941.9778328195844 - dF predicted:0.019394517057263964
-iteration: 19 - F:2941.9930116377955 - dF predicted:0.02114624714156537
-iteration: 20 - F:2942.012944005637 - dF predicted:0.024016857226357644
-convergence

Plot Results

Plot the free energy evolution over optimization iterations:

freeenergy(state)
Example block output

Plot the estimated posterior of the effective connectivity and compare that to the true parameter values. Bar hight are the posterior mean and error bars are the standard deviation of the posterior.

ecbarplot(state, setup, A_true)
Example block output

References

[1] Novelli, Leonardo, Karl Friston, and Adeel Razi. “Spectral Dynamic Causal Modeling: A Didactic Introduction and Its Relationship with Functional Connectivity.” Network Neuroscience 8, no. 1 (April 1, 2024): 178–202.
[2] Hofmann, David, Anthony G. Chesebro, Chris Rackauckas, Lilianne R. Mujica-Parodi, Karl J. Friston, Alan Edelman, and Helmut H. Strey. “Leveraging Julia’s Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed.” bioRxiv: The Preprint Server for Biology, 2023.
[3] Friston, Karl J., Joshua Kahan, Bharat Biswal, and Adeel Razi. “A DCM for Resting State fMRI.” NeuroImage 94 (July 2014): 396–407.


This page was generated using Literate.jl.

+end
iteration: 1 - F:0.0 - dF predicted:1025.1395333814698
+iteration: 2 - F:642.4783381646589 - dF predicted:501.9269486861645
+iteration: 3 - F:1082.3401875099275 - dF predicted:496.9685665015957
+iteration: 4 - F:1511.3402169533742 - dF predicted:484.192392285483
+iteration: 5 - F:1929.4051722579602 - dF predicted:460.1399605858249
+iteration: 6 - F:2313.191073080327 - dF predicted:344.7107134420372
+iteration: 7 - F:2578.327558268694 - dF predicted:173.85429717700026
+iteration: 8 - F:2708.8619714558486 - dF predicted:79.66806609589968
+iteration: 9 - F:2768.632342176919 - dF predicted:28.44581192421177
+iteration: 10 - F:2788.9840226636034 - dF predicted:8.284865925657874
+iteration: 11 - F:2794.381869452128 - dF predicted:1.3135562830852274
+iteration: 12 - F:2795.155523854636 - dF predicted:0.23238837896301912
+iteration: 13 - F:2795.239140739608 - dF predicted:0.2172993531960553
+iteration: 14 - F:2795.239140739608 - dF predicted:0.005272255044979492
+iteration: 15 - F:2795.2440370327254 - dF predicted:0.006538963767818035
+iteration: 16 - F:2795.2480494298343 - dF predicted:0.007866324178913853
+iteration: 17 - F:2795.254301400737 - dF predicted:0.008528363158614353
+convergence

Plot Results

Plot the free energy evolution over optimization iterations:

freeenergy(state)
Example block output

Plot the estimated posterior of the effective connectivity and compare that to the true parameter values. Bar hight are the posterior mean and error bars are the standard deviation of the posterior.

ecbarplot(state, setup, A_true)
Example block output

References

[1] Novelli, Leonardo, Karl Friston, and Adeel Razi. “Spectral Dynamic Causal Modeling: A Didactic Introduction and Its Relationship with Functional Connectivity.” Network Neuroscience 8, no. 1 (April 1, 2024): 178–202.
[2] Hofmann, David, Anthony G. Chesebro, Chris Rackauckas, Lilianne R. Mujica-Parodi, Karl J. Friston, Alan Edelman, and Helmut H. Strey. “Leveraging Julia’s Automated Differentiation and Symbolic Computation to Increase Spectral DCM Flexibility and Speed.” bioRxiv: The Preprint Server for Biology, 2023.
[3] Friston, Karl J., Joshua Kahan, Bharat Biswal, and Adeel Razi. “A DCM for Resting State fMRI.” NeuroImage 94 (July 2014): 396–407.


This page was generated using Literate.jl.