Skip to content

Commit

Permalink
Update documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
actions-user committed Oct 1, 2024
1 parent d2fe198 commit 8d35fd8
Show file tree
Hide file tree
Showing 118 changed files with 2,189 additions and 283 deletions.
Binary file modified _downloads/0025a4eb2d8e30fa52adf9b04117ce45/rcca.zip
Binary file not shown.
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 _downloads/315c4c52fb68082a731b192d944e2ede/tutorials_python.zip
Binary file not shown.
Binary file modified _downloads/36b2988b6eab4411b3f5219317ec938a/ecca.zip
Binary file not shown.
Binary file modified _downloads/769bc627a5758c3c1610e90a2d526da1/etrca.zip
Binary file not shown.
Binary file modified _downloads/7e3f727e42c00cd1c0d378eda9702c82/early_stopping.zip
Binary file not shown.
Binary file not shown.
Binary file modified _downloads/bc82bea3a5dd7bdba60b65220891d9e5/examples_python.zip
Binary file not shown.
167 changes: 167 additions & 0 deletions _downloads/bcef6948460f4c19e9a28ed9951bb3fa/optimize_stimulus.py
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 modified _downloads/bd7891e75b99944aca310b0f0d94d251/fbrcca.zip
Binary file not shown.
Binary file not shown.
Binary file modified _downloads/e577358a0bd09b529f2869bb63375af0/epoch_cca_lda.zip
Binary file not shown.
Binary file not shown.
Binary file modified _downloads/fb625db3c50d423b1b7881136ffdeec8/examples_jupyter.zip
Binary file not shown.
Binary file not shown.
Binary file added _images/sphx_glr_optimize_stimulus_001.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/sphx_glr_optimize_stimulus_002.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/sphx_glr_optimize_stimulus_003.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/sphx_glr_optimize_stimulus_004.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/sphx_glr_optimize_stimulus_thumb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions _modules/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Overview: module code &mdash; PyntBCI 1.5.0 documentation</title>
<title>Overview: module code &mdash; PyntBCI 1.6.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=fa44fd50" />
<link rel="stylesheet" type="text/css" href="../_static/css/theme.css?v=19f00094" />
<link rel="stylesheet" type="text/css" href="../_static/sg_gallery.css?v=d2d258e8" />
Expand All @@ -18,7 +18,7 @@

<script src="../_static/jquery.js?v=5d32c60e"></script>
<script src="../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="../_static/documentation_options.js?v=e0a75244"></script>
<script src="../_static/documentation_options.js?v=72d88caf"></script>
<script src="../_static/doctools.js?v=9a2dae69"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../_static/js/theme.js"></script>
Expand All @@ -38,7 +38,7 @@
PyntBCI
</a>
<div class="version">
1.5.0
1.6.0
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
Expand Down
6 changes: 3 additions & 3 deletions _modules/pyntbci/classifiers.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>pyntbci.classifiers &mdash; PyntBCI 1.5.0 documentation</title>
<title>pyntbci.classifiers &mdash; PyntBCI 1.6.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=fa44fd50" />
<link rel="stylesheet" type="text/css" href="../../_static/css/theme.css?v=19f00094" />
<link rel="stylesheet" type="text/css" href="../../_static/sg_gallery.css?v=d2d258e8" />
Expand All @@ -18,7 +18,7 @@

<script src="../../_static/jquery.js?v=5d32c60e"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="../../_static/documentation_options.js?v=e0a75244"></script>
<script src="../../_static/documentation_options.js?v=72d88caf"></script>
<script src="../../_static/doctools.js?v=9a2dae69"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/js/theme.js"></script>
Expand All @@ -38,7 +38,7 @@
PyntBCI
</a>
<div class="version">
1.5.0
1.6.0
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
Expand Down
6 changes: 3 additions & 3 deletions _modules/pyntbci/envelope.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>pyntbci.envelope &mdash; PyntBCI 1.5.0 documentation</title>
<title>pyntbci.envelope &mdash; PyntBCI 1.6.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=fa44fd50" />
<link rel="stylesheet" type="text/css" href="../../_static/css/theme.css?v=19f00094" />
<link rel="stylesheet" type="text/css" href="../../_static/sg_gallery.css?v=d2d258e8" />
Expand All @@ -18,7 +18,7 @@

<script src="../../_static/jquery.js?v=5d32c60e"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="../../_static/documentation_options.js?v=e0a75244"></script>
<script src="../../_static/documentation_options.js?v=72d88caf"></script>
<script src="../../_static/doctools.js?v=9a2dae69"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/js/theme.js"></script>
Expand All @@ -38,7 +38,7 @@
PyntBCI
</a>
<div class="version">
1.5.0
1.6.0
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
Expand Down
6 changes: 3 additions & 3 deletions _modules/pyntbci/gates.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>pyntbci.gates &mdash; PyntBCI 1.5.0 documentation</title>
<title>pyntbci.gates &mdash; PyntBCI 1.6.0 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css?v=fa44fd50" />
<link rel="stylesheet" type="text/css" href="../../_static/css/theme.css?v=19f00094" />
<link rel="stylesheet" type="text/css" href="../../_static/sg_gallery.css?v=d2d258e8" />
Expand All @@ -18,7 +18,7 @@

<script src="../../_static/jquery.js?v=5d32c60e"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="../../_static/documentation_options.js?v=e0a75244"></script>
<script src="../../_static/documentation_options.js?v=72d88caf"></script>
<script src="../../_static/doctools.js?v=9a2dae69"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/js/theme.js"></script>
Expand All @@ -38,7 +38,7 @@
PyntBCI
</a>
<div class="version">
1.5.0
1.6.0
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
Expand Down
Loading

0 comments on commit 8d35fd8

Please sign in to comment.