-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d2fe198
commit 8d35fd8
Showing
118 changed files
with
2,189 additions
and
283 deletions.
There are no files selected for viewing
Binary file not shown.
97 changes: 97 additions & 0 deletions
97
_downloads/0cf573ab93332f6d2dfcd75aad74cc2d/optimize_stimulus.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"\n# Optimize stimulus subset and layout\nThis script shows how to optimize the stimulus presentation by means of selecting the optimal subset of stimuli from a\nset of candidate stimuli and how to select an optimal layout to allocate them to a stimulus grid. The methods to\noptimize such subset and layout were developed and evaluated in [1]_.\n\nThe data used in this script are simulated.\n\n## References\n.. [1] Thielen, J., Van Den Broek, P., Farquhar, J., & Desain, P. (2015). Broad-Band visually evoked potentials:\n re(con)volution in brain-computer interfacing. PloS one, 10(7), e0133797.\n DOI: https://doi.org/10.1371/journal.pone.0133797\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"import matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\n\nimport pyntbci\n\nsns.set_context(\"paper\", font_scale=1.5)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Simulated data\nThe cell below generated synthetic data. Specifically, we will generate a set of modulated Gold codes and use a\nconvolution with a synthetic flash-VEP to generate simulated EEG template responses.\n\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"fs = 120 # Hertz\npr = 60 # Hertz\namplitude = 1.0 # microvolts\nwidth = 0.020 # seconds\nlatency = 0.100 # seconds\nencoding_length = 0.3 # seconds\nn_channels = 1\nsnr = 0.5\n\n# Generate codes\nV = pyntbci.stimulus.make_gold_codes()\nV = pyntbci.stimulus.modulate(V)\nV = V.repeat(int(fs / pr), axis=1)\nn_codes, n_samples = V.shape\n\n# Generate structure matrices\nM = pyntbci.utilities.encoding_matrix(V[:, np.newaxis, :], int(encoding_length * fs))\n\n# Generate flash-VEP\ntime = np.arange(0, encoding_length, 1/fs)\nr = amplitude * (1 - ((time - latency) / width)**2) * np.exp(-0.5 * ((time - latency) / width)**2)\n\n# Generate template responses\nT = r.dot(M.transpose((1, 0, 2)).reshape((-1, n_codes * n_samples))).reshape(n_codes, n_samples)\nT /= np.std(T, axis=1, keepdims=True) # normalize amplitudes" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Optimize stimulus subset\nThe cell above generated 63 different codes and for each an expected template EEG response. In the following we assume\nwe have a 4 x 8 matrix speller setup, for a total of 32 classes. Thus, we can select an optimal subset of 32 codes\nfrom the 63 available codes. This we will do by minimizing the maximum pair-wise correlation between templates within\nthe subset.\n\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"n_random = 100000 # number of random \"optimizations\"\n\n# Assumed speller matrix\nmatrix = np.arange(32).reshape(4, 8)\nn_classes = matrix.size\n\n# Compute correlation matrix\nrho = pyntbci.utilities.correlation(T, T)\nrho[np.eye(rho.shape[0]) == 1] = np.nan\n\n# Optimize subset\noptimized_subset = pyntbci.stimulus.optimize_subset_clustering(T, n_classes)\noptimized = np.nanmax(rho[optimized_subset, :][:, optimized_subset])\noptimized_vals = rho[optimized_subset, :][:, optimized_subset].flatten()\noptimized_vals = optimized_vals[~np.isnan(optimized_vals)]\n\n# Random subset\nrandom_subset = []\nvalue = 1 # maximum correlation\nrandom = np.zeros(n_random)\nfor i in range(n_random):\n subset_ = np.random.permutation(T.shape[0])[:n_classes]\n random[i] = np.nanmax(rho[subset_, :][:, subset_])\n if random[i] < value:\n random_subset = subset_\n value = random[i]\nrandom_vals = rho[random_subset, :][:, random_subset].flatten()\nrandom_vals = random_vals[~np.isnan(random_vals)]\n\n# Visualize tested and optimized layouts\ncolors = sns.color_palette(\"colorblind\")\nplt.figure(figsize=(15, 3))\nplt.axvline(optimized, color=colors[0], label=\"optimized\")\nplt.axvline(random.min(), color=colors[1], label=f\"best random (N={n_random})\")\nplt.hist(random, color=colors[2], label=\"maximum within random layout\")\nplt.legend()\nplt.xlabel(\"maximum correlation across layouts\")\nplt.ylabel(\"count\")\n\n# Visualize optimized layouts\ncolors = sns.color_palette(\"colorblind\")\nplt.figure(figsize=(15, 3))\nplt.hist(optimized_vals, 10, alpha=0.6, color=colors[0], label=\"optimized\")\nplt.hist(random_vals, 10, alpha=0.6, color=colors[1], label=f\"best random (N={n_random})\")\nplt.legend()\nplt.xlabel(\"maximum correlation within layouts\")\nplt.ylabel(\"norm. count\")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Optimize stimulus layout\nNow we have the optimal subset of 32 codes. Still, we could optimize how these are allocated to the 4 x 8 speller\ngrid, such that codes that still correlate much are not placed at neighbouring cells in the grid.\n\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"# Select optimize subset\nT = T[optimized_subset, :]\n\n# Compute correlation matrix\nrho = pyntbci.utilities.correlation(T, T)\nrho[np.eye(rho.shape[0]) == 1] = np.nan\n\n# Create neighbours matrix assuming 4 x 8 grid\nneighbours = pyntbci.utilities.find_neighbours(matrix)\n\n# Optimize layout\noptimized_layout = pyntbci.stimulus.optimize_layout_incremental(T, neighbours, 50, 50)\noptimized = np.nanmax(rho[optimized_layout[neighbours[:, 0]], optimized_layout[neighbours[:, 1]]])\noptimized_vals = rho[optimized_layout[neighbours[:, 0]], optimized_layout[neighbours[:, 1]]].flatten()\noptimized_vals = optimized_vals[~np.isnan(optimized_vals)]\n\n# Random layout\nrandom_layout = []\nvalue = 1 # maximum correlation\nrandom = np.zeros(n_random)\nfor i in range(n_random):\n layout_ = np.random.permutation(T.shape[0])\n random[i] = np.nanmax(rho[layout_[neighbours[:, 0]], layout_[neighbours[:, 1]]])\n if random[i] < value:\n random_layout = layout_\n value = random[i]\nrandom_vals = rho[random_layout[neighbours[:, 0]], random_layout[neighbours[:, 1]]].flatten()\nrandom_vals = random_vals[~np.isnan(random_vals)]\n\n# Visualize tested and optimized layouts\ncolors = sns.color_palette(\"colorblind\")\nplt.figure(figsize=(15, 3))\nplt.axvline(optimized, color=colors[0], label=\"optimized\")\nplt.axvline(random.min(), color=colors[1], label=f\"best random (N={n_random})\")\nplt.hist(random, color=colors[2], label=\"maximum within random layout\")\nplt.legend()\nplt.xlabel(\"maximum correlation across layouts\")\nplt.ylabel(\"count\")\n\n# Visualize optimized layouts\ncolors = sns.color_palette(\"colorblind\")\nplt.figure(figsize=(15, 3))\nplt.hist(optimized_vals, 10, alpha=0.6, color=colors[0], label=\"optimized\")\nplt.hist(random_vals, 10, alpha=0.6, color=colors[1], label=f\"best random (N={n_random})\")\nplt.legend()\nplt.xlabel(\"maximum correlation within layouts\")\nplt.ylabel(\"norm. count\")\n\n# plt.show()" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.10.12" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 0 | ||
} |
Binary file modified
BIN
+0 Bytes
(100%)
_downloads/315c4c52fb68082a731b192d944e2ede/tutorials_python.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified
BIN
+0 Bytes
(100%)
_downloads/7e3f727e42c00cd1c0d378eda9702c82/early_stopping.zip
Binary file not shown.
Binary file modified
BIN
+0 Bytes
(100%)
_downloads/a5659940aa3f8f568547d47752a43172/tutorials_jupyter.zip
Binary file not shown.
Binary file modified
BIN
+6.16 KB
(110%)
_downloads/bc82bea3a5dd7bdba60b65220891d9e5/examples_python.zip
Binary file not shown.
167 changes: 167 additions & 0 deletions
167
_downloads/bcef6948460f4c19e9a28ed9951bb3fa/optimize_stimulus.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
""" | ||
Optimize stimulus subset and layout | ||
=================== | ||
This script shows how to optimize the stimulus presentation by means of selecting the optimal subset of stimuli from a | ||
set of candidate stimuli and how to select an optimal layout to allocate them to a stimulus grid. The methods to | ||
optimize such subset and layout were developed and evaluated in [1]_. | ||
The data used in this script are simulated. | ||
References | ||
---------- | ||
.. [1] Thielen, J., Van Den Broek, P., Farquhar, J., & Desain, P. (2015). Broad-Band visually evoked potentials: | ||
re(con)volution in brain-computer interfacing. PloS one, 10(7), e0133797. | ||
DOI: https://doi.org/10.1371/journal.pone.0133797 | ||
""" | ||
|
||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
import seaborn as sns | ||
|
||
import pyntbci | ||
|
||
sns.set_context("paper", font_scale=1.5) | ||
|
||
# %% | ||
# Simulated data | ||
# ----------------- | ||
# The cell below generated synthetic data. Specifically, we will generate a set of modulated Gold codes and use a | ||
# convolution with a synthetic flash-VEP to generate simulated EEG template responses. | ||
|
||
fs = 120 # Hertz | ||
pr = 60 # Hertz | ||
amplitude = 1.0 # microvolts | ||
width = 0.020 # seconds | ||
latency = 0.100 # seconds | ||
encoding_length = 0.3 # seconds | ||
n_channels = 1 | ||
snr = 0.5 | ||
|
||
# Generate codes | ||
V = pyntbci.stimulus.make_gold_codes() | ||
V = pyntbci.stimulus.modulate(V) | ||
V = V.repeat(int(fs / pr), axis=1) | ||
n_codes, n_samples = V.shape | ||
|
||
# Generate structure matrices | ||
M = pyntbci.utilities.encoding_matrix(V[:, np.newaxis, :], int(encoding_length * fs)) | ||
|
||
# Generate flash-VEP | ||
time = np.arange(0, encoding_length, 1/fs) | ||
r = amplitude * (1 - ((time - latency) / width)**2) * np.exp(-0.5 * ((time - latency) / width)**2) | ||
|
||
# Generate template responses | ||
T = r.dot(M.transpose((1, 0, 2)).reshape((-1, n_codes * n_samples))).reshape(n_codes, n_samples) | ||
T /= np.std(T, axis=1, keepdims=True) # normalize amplitudes | ||
|
||
# %% | ||
# Optimize stimulus subset | ||
# ----------------- | ||
# The cell above generated 63 different codes and for each an expected template EEG response. In the following we assume | ||
# we have a 4 x 8 matrix speller setup, for a total of 32 classes. Thus, we can select an optimal subset of 32 codes | ||
# from the 63 available codes. This we will do by minimizing the maximum pair-wise correlation between templates within | ||
# the subset. | ||
|
||
n_random = 100000 # number of random "optimizations" | ||
|
||
# Assumed speller matrix | ||
matrix = np.arange(32).reshape(4, 8) | ||
n_classes = matrix.size | ||
|
||
# Compute correlation matrix | ||
rho = pyntbci.utilities.correlation(T, T) | ||
rho[np.eye(rho.shape[0]) == 1] = np.nan | ||
|
||
# Optimize subset | ||
optimized_subset = pyntbci.stimulus.optimize_subset_clustering(T, n_classes) | ||
optimized = np.nanmax(rho[optimized_subset, :][:, optimized_subset]) | ||
optimized_vals = rho[optimized_subset, :][:, optimized_subset].flatten() | ||
optimized_vals = optimized_vals[~np.isnan(optimized_vals)] | ||
|
||
# Random subset | ||
random_subset = [] | ||
value = 1 # maximum correlation | ||
random = np.zeros(n_random) | ||
for i in range(n_random): | ||
subset_ = np.random.permutation(T.shape[0])[:n_classes] | ||
random[i] = np.nanmax(rho[subset_, :][:, subset_]) | ||
if random[i] < value: | ||
random_subset = subset_ | ||
value = random[i] | ||
random_vals = rho[random_subset, :][:, random_subset].flatten() | ||
random_vals = random_vals[~np.isnan(random_vals)] | ||
|
||
# Visualize tested and optimized layouts | ||
colors = sns.color_palette("colorblind") | ||
plt.figure(figsize=(15, 3)) | ||
plt.axvline(optimized, color=colors[0], label="optimized") | ||
plt.axvline(random.min(), color=colors[1], label=f"best random (N={n_random})") | ||
plt.hist(random, color=colors[2], label="maximum within random layout") | ||
plt.legend() | ||
plt.xlabel("maximum correlation across layouts") | ||
plt.ylabel("count") | ||
|
||
# Visualize optimized layouts | ||
colors = sns.color_palette("colorblind") | ||
plt.figure(figsize=(15, 3)) | ||
plt.hist(optimized_vals, 10, alpha=0.6, color=colors[0], label="optimized") | ||
plt.hist(random_vals, 10, alpha=0.6, color=colors[1], label=f"best random (N={n_random})") | ||
plt.legend() | ||
plt.xlabel("maximum correlation within layouts") | ||
plt.ylabel("norm. count") | ||
|
||
# %% | ||
# Optimize stimulus layout | ||
# ----------------- | ||
# Now we have the optimal subset of 32 codes. Still, we could optimize how these are allocated to the 4 x 8 speller | ||
# grid, such that codes that still correlate much are not placed at neighbouring cells in the grid. | ||
|
||
# Select optimize subset | ||
T = T[optimized_subset, :] | ||
|
||
# Compute correlation matrix | ||
rho = pyntbci.utilities.correlation(T, T) | ||
rho[np.eye(rho.shape[0]) == 1] = np.nan | ||
|
||
# Create neighbours matrix assuming 4 x 8 grid | ||
neighbours = pyntbci.utilities.find_neighbours(matrix) | ||
|
||
# Optimize layout | ||
optimized_layout = pyntbci.stimulus.optimize_layout_incremental(T, neighbours, 50, 50) | ||
optimized = np.nanmax(rho[optimized_layout[neighbours[:, 0]], optimized_layout[neighbours[:, 1]]]) | ||
optimized_vals = rho[optimized_layout[neighbours[:, 0]], optimized_layout[neighbours[:, 1]]].flatten() | ||
optimized_vals = optimized_vals[~np.isnan(optimized_vals)] | ||
|
||
# Random layout | ||
random_layout = [] | ||
value = 1 # maximum correlation | ||
random = np.zeros(n_random) | ||
for i in range(n_random): | ||
layout_ = np.random.permutation(T.shape[0]) | ||
random[i] = np.nanmax(rho[layout_[neighbours[:, 0]], layout_[neighbours[:, 1]]]) | ||
if random[i] < value: | ||
random_layout = layout_ | ||
value = random[i] | ||
random_vals = rho[random_layout[neighbours[:, 0]], random_layout[neighbours[:, 1]]].flatten() | ||
random_vals = random_vals[~np.isnan(random_vals)] | ||
|
||
# Visualize tested and optimized layouts | ||
colors = sns.color_palette("colorblind") | ||
plt.figure(figsize=(15, 3)) | ||
plt.axvline(optimized, color=colors[0], label="optimized") | ||
plt.axvline(random.min(), color=colors[1], label=f"best random (N={n_random})") | ||
plt.hist(random, color=colors[2], label="maximum within random layout") | ||
plt.legend() | ||
plt.xlabel("maximum correlation across layouts") | ||
plt.ylabel("count") | ||
|
||
# Visualize optimized layouts | ||
colors = sns.color_palette("colorblind") | ||
plt.figure(figsize=(15, 3)) | ||
plt.hist(optimized_vals, 10, alpha=0.6, color=colors[0], label="optimized") | ||
plt.hist(random_vals, 10, alpha=0.6, color=colors[1], label=f"best random (N={n_random})") | ||
plt.legend() | ||
plt.xlabel("maximum correlation within layouts") | ||
plt.ylabel("norm. count") | ||
|
||
# plt.show() |
Binary file not shown.
Binary file modified
BIN
+0 Bytes
(100%)
_downloads/d7841fcf7aeee1e534b47e110dcd24b8/tutorial_reconvolution_events.zip
Binary file not shown.
Binary file modified
BIN
+0 Bytes
(100%)
_downloads/e577358a0bd09b529f2869bb63375af0/epoch_cca_lda.zip
Binary file not shown.
Binary file not shown.
Binary file modified
BIN
+7.85 KB
(110%)
_downloads/fb625db3c50d423b1b7881136ffdeec8/examples_jupyter.zip
Binary file not shown.
Binary file modified
BIN
+0 Bytes
(100%)
_downloads/fea03dc290f0a0baaa67ba00902e368f/tutorial_getting_started.zip
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.