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

componentfunction #91

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ version = "0.3.2"

[deps]
Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
Automa = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b"
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Expand Down
39 changes: 39 additions & 0 deletions docs/literate/HowTo/componentfunction.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# # Component Functions
# HowTo put arbitrary functions into components

using UnfoldSim
using Unfold
using Random
using DSP
using CairoMakie, UnfoldMakie

sfreq = 100;

# ## Design
# Let's generate a design with a categorical effect and a continuous duration effect
design = UnfoldSim.SingleSubjectDesign(;
conditions = Dict(
:category => ["dog", "cat"],
:duration => Int.(round.(20 .+ rand(100) .* sfreq)),
),
);


# Instead of defining a boring vector basis function e.g. `[0,0,1,2,3,3,2,1,0,0,0]`, let's use function, generating random values for now.
# !!! important
# because any function depending on `design` can be used, two things have to be taken care of:
#
# 1. in case a random component exist, specify a `RNG`, the basis might be evaluated multiple times inside `simulate`
# 2. a `maxlength` has to be specified via a tuple `(function.maxlength)``
mybasisfun = design -> hanning.(generate_events(design).duration)
signal = LinearModelComponent(;
basis = (mybasisfun, 100),
formula = @formula(0 ~ 1 + category),
β = [1, 0.5],
);

erp = UnfoldSim.simulate_component(MersenneTwister(1), signal, design);


# Finally, let's plot it, sorted by duration
plot_erpimage(erp, sortvalues = generate_events(design).duration)
3 changes: 3 additions & 0 deletions docs/literate/HowTo/newComponent.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# # New component: Duration + Shift

# We want a new component that changes its duration and shift depending on a column in the event-design. This is somewhat already implemented in the HRF + Pupil bases
# !!! hint
# if you are just interested to use duration-dependency in your simulation, check out the component-function tutorial

using UnfoldSim
using Unfold
using Random
Expand Down
68 changes: 68 additions & 0 deletions docs/literate/HowTo/sequence.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Base: add_sum
using UnfoldSim
using CairoMakie
using StableRNGs

# ## Stimulus - Response design

# let's say we want to simulate a stimulus response, followed by a button press response.
# First we generate the minimal design of the experiment by specifying our conditins (a one-condition-two-levels design in our case)
design = SingleSubjectDesign(conditions = Dict(:condition => ["one", "two"]))#|>x->RepeatDesign(x,4)
generate_events(design)
# next we use the `SequenceDesign` and nest our initial design in it. "SR_" is code for an "S" event and an "R" event - only single letter events are supported! The `_` is a signal for the Onset generator to generate a bigger pause - no overlap between adjacend `SR` pairs
design = SequenceDesign(design, "SR{1,2}_", 0, StableRNG(1))
generate_events(design)
# The main thing that happened is that the design was repeated for every event (each 'letter') of the sequence, and an `eventtype` column was added.
# !!! hint
# more advaned sequences are possible as well, like "SR{1,3}", or "A[BC]". Infinite sequences are not possible like "AB*"

# Finally, let's repeat the design 2 times - because we can
design = RepeatDesign(design, 4)
generate_events(design)

#design = UnfoldSim.AddSaccadeAmplitudeDesign4(design,:rt,Normal(0,1),MersenneTwister(1))
#generate_events(design)


# This results in 12 trials that nicely follow our sequence

# Next we have to specify for both events `S` and `R` what the responses should look like.

p1 = LinearModelComponent(;
basis = p100(),
formula = @formula(0 ~ 1 + condition),
β = [1, 0.5],
);

n1 = LinearModelComponent(;
basis = n170(),
formula = @formula(0 ~ 1 + condition),
β = [1, 0.5],
);
p3 = LinearModelComponent(;
basis = UnfoldSim.hanning(Int(0.5 * 100)), # sfreq = 100 for the other bases
formula = @formula(0 ~ 1 + condition),
β = [1, 0],
);

resp = LinearModelComponent(;
basis = UnfoldSim.hanning(Int(0.5 * 100)), # sfreq = 100 for the other bases
formula = @formula(0 ~ 1 + condition),
β = [1, 2],
offset = -10,
);

components = Dict('S' => [p1, n1, p3], 'R' => [resp])
#components = [p1, n1, resp]
data, evts = simulate(
StableRNG(1),
design,
components,
UniformOnset(offset = 40, width = 10),
NoNoise(),
)

lines(data)
vlines!(evts.latency, color = (:gray, 0.5))
xlims!(0, 500)
current_figure()
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
current_figure()
current_figure()

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
current_figure()
current_figure()

22 changes: 22 additions & 0 deletions docs/literate/reference/onsettypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,25 @@ end
# - if `offset` < `length(signal.basis)` -> there might be overlap, depending on the other parameters of the onset distribution

# [^1]: Wikipedia contributors. (2023, December 5). Log-normal distribution. In Wikipedia, The Free Encyclopedia. Retrieved 12:27, December 7, 2023, from https://en.wikipedia.org/w/index.php?title=Log-normal_distribution&oldid=1188400077#




# ## Design-dependent `FormulaXOnset`

# For additional control we provide `FormulaUniformOnset` and `FormulaLogNormalOnset` types, that allow to control all parameters by specifying formulas
o = UnfoldSim.FormulaUniformOnset(
width_formula = @formula(0 ~ 1 + cond),
width_β = [50, 20],
)
events = generate_events(design)
onsets = UnfoldSim.simulate_interonset_distances(MersenneTwister(42), o, design)

f = Figure()
ax = f[1, 1] = Axis(f)
hist!(ax, onsets[events.cond.=="A"], bins = range(0, 100, step = 1), label = "cond: A")
hist!(ax, onsets[events.cond.=="B"], bins = range(0, 100, step = 1), label = "cond: B")
axislegend(ax)
f

# Voila - the inter-onset intervals are `20` samples longer for condition `B`, exactly as specified.`
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
# Voila - the inter-onset intervals are `20` samples longer for condition `B`, exactly as specified.`
# Voila - the inter-onset intervals are `20` samples longer for condition `B`, exactly as specified.`

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
# Voila - the inter-onset intervals are `20` samples longer for condition `B`, exactly as specified.`
# Voila - the inter-onset intervals are `20` samples longer for condition `B`, exactly as specified.`

4 changes: 3 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ makedocs(;
],
"HowTo" => [
"Define a new, (imbalanced) design" => "./generated/HowTo/newDesign.md",
"Use a component-basis-function (duration)" => "./generated/HowTo/componentfunction.md",
"Repeating a design" => "./generated/HowTo/repeatTrials.md",
"Define a new duration & jitter component" => "./generated/HowTo/newComponent.md",
"Define a new component" => "./generated/HowTo/newComponent.md",
"Generate multi channel data" => "./generated/HowTo/multichannel.md",
"Use predefined design / onsets data" => "./generated/HowTo/predefinedData.md",
"Produce specific sequences of events" => "./generated/HowTo/sequence.md",
],
"API / DocStrings" => "api.md",
],
Expand Down
4 changes: 3 additions & 1 deletion src/UnfoldSim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ using LinearAlgebra
using ToeplitzMatrices # for AR Expo. Noise "Circulant"
using StatsModels
using HDF5, Artifacts, FileIO
using Automa # for sequence

using LinearAlgebra # headmodel

Expand All @@ -30,6 +31,7 @@ include("onset.jl")
include("predefinedSimulations.jl")
include("headmodel.jl")
include("helper.jl")
include("sequence.jl")
include("bases.jl")

export size, length
Expand All @@ -46,7 +48,7 @@ export Simulation
export MixedModelComponent, LinearModelComponent

# export designs
export MultiSubjectDesign, SingleSubjectDesign, RepeatDesign
export MultiSubjectDesign, SingleSubjectDesign, RepeatDesign, SequenceDesign

# noise functions
export PinkNoise, RedNoise, WhiteNoise, NoNoise, ExponentialNoise #,RealNoise (not implemented yet)
Expand Down
Loading
Loading