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

orientation tutorial #131

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
91 changes: 91 additions & 0 deletions docs/literate/explanations/orientations_headmodels.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# # On orientations in Leadfields for simulation

# This is a somewhat loose organisation of some thoughts we had regarding leadfields, simulations with a focus on orientation of sources

# ### Setup
# ```@raw html
# <details>
# <summary>Click to expand</summary>
# ```
## Load required packages

using CairoMakie
using UnfoldMakie
using UnfoldSim
using LinearAlgebra: norm
# ```@raw html
# </details >
# ```

# ### The leadfield
hart = headmodel()
pos = to_positions(hart.electrodes["pos"]')
L = leadfield(hart)
size(L)

# The leadfield describes the connection between source-points and electrodes. It contains the precalculated forward simulation of the electrical potentials.
# As seen from the size, we actually have three leadfields, one for `x`, one for `y `and one for the `z` direction.
#
# The question we will explore is: Which direction should we simulate.
#
# Let's first plot the three orientations so we know what we are talking about
f = Figure()
ix = 50
[
plot_topoplot!(
f[1, k],
L[:, ix, k];
positions = pos,
visual = (; label_scatter = false),
) for k = 1:3
]
f

# These are the simulated source activatios of the source-point `$(ix)`, if the underlying dipolar source would be oriented in x,y, or z direction.

# But often, we do not want that, but want to simulate the "best" direction. But how to get that?

# Luckily, often headmodellers provide orientations orthogonal to the cortex, in direction of the pyramidal cells - a physiological plausible prior.
# This is the default we use

m_perp = magnitude(hart)
plot_topoplot!(
f[2, 2],
m_perp[:, ix];
positions = pos,
visual = (; label_scatter = false),
axis = (; title = "perpendicular"),
)
f

# We could also calculate the vector norm:

m_norm = mapslices(norm, L; dims = (3))
ix = 50
plot_topoplot!(
f[2, 1],
m_norm[:, ix, 1];
positions = pos,
visual = (; label_scatter = false),
axis = (; title = "norm"),
)
f

# other options are sum, or maximum, or ...
plot_topoplot!(
f[2, 3],
sum(L[:, ix, :], dims = 2)[:, 1];
positions = pos,
visual = (; label_scatter = false),
axis = (; title = "sum"),
)
plot_topoplot!(
f[3, 3],
maximum(L[:, ix, :], dims = 2)[:, 1];
positions = pos,
visual = (; label_scatter = false),
axis = (; title = "max"),
)
f

# Which is the best? We don't know! If you have good ideas & your own headmodels please tell us. For now we recommend just to use "perpendicular" :)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
# Which is the best? We don't know! If you have good ideas & your own headmodels please tell us. For now we recommend just to use "perpendicular" :)
# Which is the best? We don't know! If you have good ideas & your own headmodels please tell us. For now we recommend just to use "perpendicular" :)

3 changes: 3 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ makedocs(;
"Generate multi channel data" => "./generated/HowTo/multichannel.md",
"Use existing experimental designs & onsets in the simulation" => "./generated/HowTo/predefinedData.md",
],
"Explanations/Explorations" => [
"Leadfield Orientations" => "./generated/explanations/orientations_headmodels.md",
],
"API / Docstrings" => "api.md",
],
)
Expand Down
36 changes: 22 additions & 14 deletions src/noise.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,19 @@ struct AutoRegressiveNoise <: AbstractNoise end
"""
ExponentialNoise <: AbstractNoise

Noise with exponential decay in AR spectrum.
Noise with exponential decay in AR spectrum. Implements the algorithm (3) from Markus Deserno 2002: https://www.cmu.edu/biolphys/deserno/pdf/corr_gaussian_random.pdf
"How to generate exponentially correlated Gaussian random numbers"

The noise has std of 1 and mean of 0 (over many samples)

The factor "τ" defines the decay over samples.

`noiselevel` is used to scale the noise

!!! warning
With the current implementation we try to get exponential decay over the whole autoregressive (AR) spectrum, which is N samples (the total number of samples in the signal) long. This involves the inversion of a Cholesky matrix of size NxN matrix, which will need lots of RAM for non-trivial problems.
"""
@with_kw struct ExponentialNoise <: AbstractNoise
noiselevel = 1
ν = 1.5 # exponential factor of AR decay "nu"
τ = 1000
end


Expand Down Expand Up @@ -116,22 +119,25 @@ end

function simulate_noise(rng, t::ExponentialNoise, n::Int)

function exponential_correlation(x; nu = 1, length_ratio = 1)
# Author: Jaromil Frossard
# generate exponential function
R = length(x) * length_ratio
return exp.(-3 * (x / R) .^ nu)
end
τ = t.τ

Σ = Symmetric(Circulant(exponential_correlation([0:1:(n-1);], nu = t.ν)), :L)

# cholesky(Σ) is n x n diagonal, lots of RAM :S
return t.noiselevel .* 10 .* (randn(rng, n)'*cholesky(Σ).U)[1, :]
end
@assert τ > 0
f = exp(-1 / τ)
#s = 0:n-1
g = randn(rng, n)
r = similar(g)
r[1] = g[1]
for n = 1:length(g)-1
r[n+1] = f * r[n] + sqrt(1 - f^2) * g[n+1]
end




return t.noiselevel .* r
end

"""
add_noise!(rng, noisetype::AbstractNoise, signal)

Expand All @@ -149,3 +155,5 @@ function add_noise!(rng, noisetype::AbstractNoise, signal)
signal .+= noise

end


Comment on lines +158 to +159
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change

Loading