Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated CMC code #474

Merged
merged 34 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
db1d221
switched from symbolic arrays to list of symbolic parameters. Needed …
david-hofmann Oct 20, 2024
eadcf6d
added new approach to set tunable flag for the connection matrix also…
david-hofmann Oct 20, 2024
bade7b2
use generate_weight_param also in StimulusBlox and ObserverBlox conne…
david-hofmann Oct 22, 2024
0cdf80e
Merge branch 'master' into fixesCMC
david-hofmann Oct 23, 2024
ccfb999
fixed and cleaned code
david-hofmann Oct 28, 2024
6b67d2d
Merge branch 'master' into fixesCMC
david-hofmann Oct 28, 2024
ea6511b
Merge branch 'master' into fixesCMC
harisorgn Oct 29, 2024
c2951b5
Add tests for AdjacencyMatrix (#477)
harisorgn Oct 29, 2024
3274bc7
More LIF fixes for the decision making tutorial (#472)
harisorgn Oct 30, 2024
a9c1591
normalize colormap range to data range (#479)
harisorgn Oct 31, 2024
2adb4ff
Ping tutorial updates (#476)
agchesebro Oct 31, 2024
7f8e3f3
Update docs based on Scott's feedback (#478)
harisorgn Nov 1, 2024
e445d79
Fix mistake in LIFExciNeuron connections (#483)
MasonProtter Nov 4, 2024
53b29c2
Make neurons from Decision Making tutorial much faster with GraphDyna…
MasonProtter Nov 5, 2024
c04b2c2
Reduce memory usage in reinforcement learning (#485)
harisorgn Nov 5, 2024
8d2fa2c
Update MTK compat (#486)
MasonProtter Nov 6, 2024
2fcd4c5
Improve type stability of reinforcement learning (#487)
harisorgn Nov 6, 2024
788f0d3
Tutorial maintenance (#488)
agchesebro Nov 8, 2024
461e7ea
DBS protocol (#481)
gabrevaya Nov 8, 2024
6967d77
minor changes
david-hofmann Nov 11, 2024
18c31b8
update MTK compat
david-hofmann Nov 11, 2024
d3f4f9f
added Project.toml to test
david-hofmann Nov 11, 2024
16a143c
fixed MTK compat once more
david-hofmann Nov 11, 2024
3584035
fixed MTK compat once more
david-hofmann Nov 11, 2024
3895e99
added more packages to test environment needed in GraphDynamics tests
david-hofmann Nov 12, 2024
86df712
remove test deps and unnecessary deps
harisorgn Nov 13, 2024
cac7a92
add Test to tect project
harisorgn Nov 13, 2024
7682da9
remove Test dep from main Project
harisorgn Nov 13, 2024
765e33a
add missing test deps
harisorgn Nov 13, 2024
a2a3194
add Statistics dep
harisorgn Nov 13, 2024
4093e6b
Merge branch 'master' into fixesCMC
harisorgn Nov 13, 2024
2ddac34
removed [extras] from Project.toml
david-hofmann Nov 13, 2024
554358b
Merge branch 'master' into fixesCMC
david-hofmann Nov 13, 2024
7c5c351
added [extras] again.
david-hofmann Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
ToeplitzMatrices = "c751599d-da0a-543b-9d20-d0a503d91d24"

[weakdeps]
Expand All @@ -57,7 +56,7 @@ GraphDynamics = "0.2"
Graphs = "1"
Interpolations = "0.14, 0.15"
MetaGraphs = "0.7"
ModelingToolkit = "9.46.0 - 9.49"
ModelingToolkit = "9.46.0 - 9.50.0"
ModelingToolkitStandardLibrary = "2"
MuladdMacro = "0.2"
OrderedCollections = "1.6.3"
Expand All @@ -84,6 +83,3 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Turing = "fce5fe82-541a-59a6-adf8-730c64b5f9a0"

[targets]
test = ["DelayDiffEq", "LinearAlgebra", "ForwardDiff", "Distributions", "Random", "SparseArrays", "MAT", "OrdinaryDiffEq", "StochasticDiffEq", "SafeTestsets", "Turing", "Test"]
19 changes: 13 additions & 6 deletions docs/src/tutorials/spectralDCM.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# # Spectral Dynamic Causal Modeling Tutorial
# # Solving Inverse Problems with Spectral Dynamic Causal Modeling
# # Introduction
#
# In this tutorial we will introduce how to perform a spectral Dynamic Causal Modeling analysis on simulated data [1,2].
# To do so we roughly resemble the procedure in the [SPM12](https://www.fil.ion.ucl.ac.uk/spm/software/spm12/) script `DEM_demo_induced_fMRI.m` in [Neuroblox](https://www.neuroblox.org/).
# 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](https://www.fil.ion.ucl.ac.uk/spm/software/spm12/) script `DEM_demo_induced_fMRI.m` in [Neuroblox](https://www.neuroblox.org/).
# This work was also presented in Hofmann et al.[2]
#
# In this tutorial we will define a circuit of three linear neuronal mass models, all driven by an Ornstein-Uhlenbeck process.
Expand Down Expand Up @@ -56,6 +60,8 @@ 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
# 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]])
end
Expand Down Expand Up @@ -127,7 +133,7 @@ for i = 1:nr
end

A_prior = 0.01*randn(nr, nr)
A_prior -= diagm(diag(A_prior)) # ensure diagonal dominance of matrix
A_prior -= diagm(diag(A_prior)) # remove the diagonal
# Since we want to optimize these weights we turn them into symbolic parameters:
# Add the symbolic weights to the edges and connect regions.
A = []
Expand All @@ -137,7 +143,7 @@ for (i, a) in enumerate(vec(A_prior))
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, [4])
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
Expand Down Expand Up @@ -201,4 +207,5 @@ ecbarplot(state, setup, A_true)

# ## 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.](https://doi.org/10.1162/netn_a_00348) \
# [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.](https://doi.org/10.1101/2023.10.27.564407)
# [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.](https://doi.org/10.1101/2023.10.27.564407) \
# [3] [Friston, Karl J., Joshua Kahan, Bharat Biswal, and Adeel Razi. “A DCM for Resting State fMRI.” NeuroImage 94 (July 2014): 396–407.](https://linkinghub.elsevier.com/retrieve/pii/S1053811913012135)
2 changes: 1 addition & 1 deletion src/Neurographs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function add_edge!(g::MetaDiGraph, p::Pair; kwargs...)
add_blox!(g, dest)
dest_idx = nv(g)
end

add_edge!(g, src_idx, dest_idx, Dict(kwargs))
end

Expand Down
23 changes: 11 additions & 12 deletions src/blox/canonicalmicrocircuit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,17 @@ mutable struct CanonicalMicroCircuitBlox <: CompositeBlox

g = MetaDiGraph()
sblox_parts = vcat(ss, sp, ii, dp)
add_blox!.(Ref(g), sblox_parts)

add_edge!(g, 1, 1, :weight, -800.0)
add_edge!(g, 2, 1, :weight, -800.0)
add_edge!(g, 3, 1, :weight, -1600.0)
add_edge!(g, 1, 2, :weight, 800.0)
add_edge!(g, 2, 2, :weight, -800.0)
add_edge!(g, 1, 3, :weight, 800.0)
add_edge!(g, 3, 3, :weight, -800.0)
add_edge!(g, 4, 3, :weight, 400.0)
add_edge!(g, 3, 4, :weight, -400.0)
add_edge!(g, 4, 4, :weight, -200.0)

add_edge!(g, ss => ss; :weight => -800.0)
add_edge!(g, sp => ss; :weight => -800.0)
add_edge!(g, ii => ss; :weight => -1600.0)
add_edge!(g, ss => sp; :weight => 800.0)
add_edge!(g, sp => sp; :weight => -800.0)
add_edge!(g, ss => ii; :weight => 800.0)
add_edge!(g, ii => ii; :weight => -800.0)
add_edge!(g, dp => ii; :weight => 400.0)
add_edge!(g, ii => dp; :weight => -400.0)
add_edge!(g, dp => dp; :weight => -200.0)

# Construct a BloxConnector object from the graph
# containing all connection equations from lower levels and this level.
Expand Down
32 changes: 9 additions & 23 deletions src/blox/connections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function generate_weight_param(blox_out, blox_in; kwargs...)
if typeof(weight) == Num # Symbol
w = weight
else
w = only(@parameters $(w_name)=weight)
w = only(@parameters $(w_name)=weight [tunable=false])
end

return w
Expand Down Expand Up @@ -351,7 +351,7 @@ function (bc::BloxConnector)(
push!(bc.weights, r)

eq = sys_in.jcn ~ sigmoid(x, r)*w

accumulate_equation!(bc, eq)
end

Expand Down Expand Up @@ -410,28 +410,20 @@ end
# additional dispatch to connect to hemodynamic observer blox
function (bc::BloxConnector)(
bloxout::NeuralMassBlox,
bloxin::ObserverBlox;
weight=1,
delay=0,
density=0.1
)
# Need t for the delay term
@variables t
bloxin::ObserverBlox;
kwargs...)

sys_out = get_namespaced_sys(bloxout)
sys_in = get_namespaced_sys(bloxin)

if typeof(bloxout.output) == Num
w_name = Symbol("w_$(nameof(sys_out))_$(nameof(sys_in))")
if typeof(weight) == Num # Symbol
w = weight
else
w = only(@parameters $(w_name)=weight [tunable=false])
end
w = generate_weight_param(bloxout, bloxin; kwargs...)
push!(bc.weights, w)
x = namespace_expr(bloxout.output, sys_out, nameof(sys_out))
eq = sys_in.jcn ~ x*w
else
# Need t for the delay term
@variables t
# Define & accumulate delay parameter
# Don't accumulate if zero
τ_name = Symbol("τ_$(nameof(sys_out))_$(nameof(sys_in))")
Expand All @@ -453,18 +445,12 @@ end
function (bc::BloxConnector)(
bloxout::StimulusBlox,
bloxin::NeuralMassBlox;
weight=1
)
kwargs...)

sys_out = get_namespaced_sys(bloxout)
sys_in = get_namespaced_sys(bloxin)

w_name = Symbol("w_$(nameof(sys_out))_$(nameof(sys_in))")
if typeof(weight) == Num # Symbol
w = weight
else
w = only(@parameters $(w_name)=weight)
end
w = generate_weight_param(bloxout, bloxin; kwargs...)
push!(bc.weights, w)

x = namespace_expr(bloxout.output, sys_out, nameof(sys_out))
Expand Down
20 changes: 20 additions & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DelayDiffEq = "bcd4f6db-9728-5f36-b5f7-82caef46ccdb"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
GraphDynamics = "bcd5d0fe-e6b7-4ef1-9848-780c183c7f4c"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MAT = "23992714-dd62-5051-b70f-ba57cb901cac"
MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5"
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
Neuroblox = "769b91e5-4c60-41ee-bfae-153c84203cb2"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
19 changes: 7 additions & 12 deletions test/datafitting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ using MAT
dt = 2.0 # time bin in seconds
freq = range(min(128, ns*dt)^-1, max(8, 2*dt)^-1, 32) # define frequencies at which to evaluate the CSD


########## assemble the model ##########
g = MetaDiGraph()
regions = Dict()
Expand All @@ -23,13 +22,11 @@ using MAT
add_blox!(g, region)
regions[ii] = nv(g) # store index of neural mass model
taskinput = ExternalInput(;name=Symbol("r$(ii)₊ei"), I=1.0)
add_blox!(g, taskinput)
add_edge!(g, nv(g), regions[ii], Dict(:weight => C))
add_edge!(g, taskinput => region, weight = C)
# add hemodynamic observer
observer = BalloonModel(;name=Symbol("r$(ii)₊bm"), lnκ=lnκ, lnϵ=lnϵ)
add_blox!(g, observer)
# connect observer with neuronal signal
add_edge!(g, regions[ii], nv(g), Dict(:weight => 1.0))
add_edge!(g, region => observer, weight = 1.0)
end

# add symbolic weights
Expand Down Expand Up @@ -130,14 +127,12 @@ end
add_blox!(g, region)
regions[ii] = nv(g) # store index of neural mass model
input = ExternalInput(;name=Symbol("r$(ii)₊ei"), I=1.0)
add_blox!(g, input)
add_edge!(g, nv(g), nv(g) - 1, Dict(:weight => C))
add_edge!(g, input => region; weight = C)

# add lead field (LFP measurement)
measurement = LeadField(;name=Symbol("r$(ii)₊lf"))
add_blox!(g, measurement)
# connect measurement with neuronal signal
add_edge!(g, nv(g) - 2, nv(g), Dict(:weight => 1.0))
add_edge!(g, region => measurement; weight = 1.0)
end

nl = Int((nrr^2-nrr)/2) # number of links unidirectional
Expand Down Expand Up @@ -191,15 +186,15 @@ end
end
indices = Dict(:dspars => collect(1:np))
# Noise parameter mean
modelparam[:lnα] = zeros(Float64, 2, nrr); # intrinsic fluctuations, ln(α) as in equation 2 of Friston et al. 2014
modelparam[:lnα] = zeros(Float64, 2, nrr); # intrinsic fluctuations, ln(α) as in equation 2 of Friston et al. 2014
n = length(modelparam[:lnα]);
indices[:lnα] = collect(np+1:np+n);
np += n;
modelparam[:lnβ] = [-16.0, -16.0]; # global observation noise, ln(β) as above
modelparam[:lnβ] = [-16.0, -16.0]; # global observation noise, ln(β) as above
n = length(modelparam[:lnβ]);
indices[:lnβ] = collect(np+1:np+n);
np += n;
modelparam[:lnγ] = [-16.0, -16.0]; # region specific observation noise
modelparam[:lnγ] = [-16.0, -16.0]; # region specific observation noise
indices[:lnγ] = collect(np+1:np+nrr);
np += nrr
indices[:u] = idx_u
Expand Down
Loading