Braindecode 1.6.1__tar.gz → 1.6.1.dev1014__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {braindecode-1.6.1/braindecode.egg-info → braindecode-1.6.1.dev1014}/PKG-INFO +1 -1
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/augmentation/base.py +19 -67
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/augmentation/functional.py +30 -44
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/__init__.py +0 -2
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/base.py +0 -109
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/eegneuralnet.py +1 -8
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/functional/__init__.py +0 -8
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/functional/functions.py +0 -159
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/__init__.py +0 -6
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/biot.py +10 -3
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/brainmodule.py +54 -185
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegitnet.py +12 -33
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegsym.py +96 -72
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/fbmsnet.py +5 -18
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/medformer.py +22 -4
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/msvtnet.py +3 -7
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/sinc_shallow.py +2 -2
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/summary.csv +60 -62
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/util.py +0 -48
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/__init__.py +1 -6
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/activation.py +0 -34
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/blocks.py +25 -100
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/filter.py +19 -80
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/layers.py +0 -126
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/preprocessing/mne_preprocess.py +12 -129
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/training/scoring.py +1 -8
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/util.py +1 -36
- braindecode-1.6.1.dev1014/braindecode/version.py +1 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014/braindecode.egg-info}/PKG-INFO +1 -1
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode.egg-info/SOURCES.txt +0 -3
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/api.rst +0 -7
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/conf.py +0 -14
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/models_table.rst +1 -4
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/whats_new.rst +1 -102
- braindecode-1.6.1/braindecode/datasets/collate.py +0 -105
- braindecode-1.6.1/braindecode/models/mvpformer.py +0 -750
- braindecode-1.6.1/braindecode/models/steegformer.py +0 -654
- braindecode-1.6.1/braindecode/version.py +0 -1
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/LICENSE.txt +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/MANIFEST.in +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/NOTICE.txt +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/README.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/__init__.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/augmentation/transforms.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/classifier.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bids/datasets.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bids/format.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bids/hub.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datautil/serialization.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/datautil/util.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/atcnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/base.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/bendr.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/codebrain.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/config.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/contrawr.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/ctnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/deep4.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegconformer.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegdino.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eeginception_erp.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eeginception_mi.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegminer.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegnex.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegpt.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/emg2qwerty.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/interpolated.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/labram.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/luna.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/meta_neuromotor.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/patchedtransformer.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/reve.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/shallow_fbcsp.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/signal_jepa.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/tcformer.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/tcn.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/models/usleep.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/attention.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/convolution.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/interpolation.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/linear.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/parametrization.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/stats.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/util.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/preprocessing/preprocess.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/preprocessing/windowers.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/regressor.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/samplers/base.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/training/__init__.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/training/losses.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/visualization/attribution.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/visualization/frequency.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/visualization/metrics.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/visualization/sanity.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode/visualization/topology.py +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode.egg-info/requires.txt +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/Makefile +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/cite.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/help.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/index.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/install/install.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/install/install_pip.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/install/install_source.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/models.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/pyproject.toml +0 -0
- {braindecode-1.6.1 → braindecode-1.6.1.dev1014}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: braindecode
|
|
3
|
-
Version: 1.6.1
|
|
3
|
+
Version: 1.6.1.dev1014
|
|
4
4
|
Summary: Deep learning software to decode EEG, ECG or MEG signals
|
|
5
5
|
Author-email: Robin Tibor Schirrmeister <robintibor@gmail.com>, Bruno Aristimunha Pinto <b.aristimunha@gmail.com>, Alexandre Gramfort <agramfort@meta.com>
|
|
6
6
|
Maintainer-email: Alexandre Gramfort <agramfort@meta.com>, Bruno Aristimunha Pinto <b.aristimunha@gmail.com>, Robin Tibor Schirrmeister <robintibor@gmail.com>
|
|
@@ -105,15 +105,7 @@ class Transform(torch.nn.Module):
|
|
|
105
105
|
mask = self._get_mask(out_X.shape[0], out_X.device)
|
|
106
106
|
num_valid = mask.sum().long()
|
|
107
107
|
|
|
108
|
-
if num_valid
|
|
109
|
-
# Fast path: every sample is augmented (e.g. ``probability=1.0``,
|
|
110
|
-
# the common case). Operate on the whole batch and skip the boolean
|
|
111
|
-
# gather/scatter (``out_X[mask]`` then ``out_X[mask] = ...``), which
|
|
112
|
-
# would otherwise copy the batch twice.
|
|
113
|
-
out_X, out_y = self.operation(
|
|
114
|
-
out_X, out_y, **self.get_augmentation_params(out_X, out_y)
|
|
115
|
-
)
|
|
116
|
-
elif num_valid > 0:
|
|
108
|
+
if num_valid > 0:
|
|
117
109
|
# Uses the mask to define the output
|
|
118
110
|
out_X[mask, ...], tr_y = self.operation(
|
|
119
111
|
out_X[mask, ...],
|
|
@@ -176,47 +168,23 @@ class Compose(Transform):
|
|
|
176
168
|
return X, y
|
|
177
169
|
|
|
178
170
|
|
|
179
|
-
|
|
180
|
-
"""
|
|
181
|
-
|
|
182
|
-
A picklable callable (so it works with ``num_workers > 0``, unlike a
|
|
183
|
-
closure) that collates the batch once and then either augments it in place
|
|
184
|
-
or grows it.
|
|
171
|
+
def _make_collateable(transform, device=None):
|
|
172
|
+
"""Wraps a transform to make it collateable.
|
|
185
173
|
|
|
186
|
-
|
|
187
|
-
----------
|
|
188
|
-
transform : Transform
|
|
189
|
-
Transform applied to the collated ``(X, y)``.
|
|
190
|
-
device : str | torch.device | None, optional
|
|
191
|
-
Device the batch is moved to before transforming. Defaults to None.
|
|
192
|
-
n_augmentation : int, optional
|
|
193
|
-
``0`` (default) applies the transform in place (batch size unchanged).
|
|
194
|
-
``> 0`` keeps the clean originals and appends ``n_augmentation``
|
|
195
|
-
independently transformed copies, returning ``(X, y)`` of
|
|
196
|
-
``(1 + n_augmentation)`` times the original size.
|
|
174
|
+
with device control.
|
|
197
175
|
"""
|
|
198
176
|
|
|
199
|
-
def
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if self.n_augmentation == 0:
|
|
211
|
-
return (*self.transform(X, y), *collated[2:])
|
|
212
|
-
# Fixed expansion: keep the clean originals, then append
|
|
213
|
-
# ``n_augmentation`` independently transformed copies.
|
|
214
|
-
xs, ys = [X], [y]
|
|
215
|
-
for _ in range(self.n_augmentation):
|
|
216
|
-
aug_X, aug_y = self.transform(X, y)
|
|
217
|
-
xs.append(aug_X)
|
|
218
|
-
ys.append(aug_y)
|
|
219
|
-
return torch.cat(xs), torch.cat(ys)
|
|
177
|
+
def _collate_fn(batch):
|
|
178
|
+
collated_batch = default_collate(batch)
|
|
179
|
+
X, y = collated_batch[:2]
|
|
180
|
+
|
|
181
|
+
if device is not None:
|
|
182
|
+
X = X.to(device)
|
|
183
|
+
y = y.to(device)
|
|
184
|
+
|
|
185
|
+
return (*transform(X, y), *collated_batch[2:])
|
|
186
|
+
|
|
187
|
+
return _collate_fn
|
|
220
188
|
|
|
221
189
|
|
|
222
190
|
class AugmentedDataLoader(DataLoader):
|
|
@@ -230,44 +198,28 @@ class AugmentedDataLoader(DataLoader):
|
|
|
230
198
|
Transform or sequence of Transform to be applied to each batch.
|
|
231
199
|
device : str | torch.device | None, optional
|
|
232
200
|
Device on which to transform the data. Defaults to None.
|
|
233
|
-
n_augmentation : int, optional
|
|
234
|
-
Number of augmented copies to append to each batch (fixed expansion).
|
|
235
|
-
When ``0`` (default) the transforms are applied in place and the batch
|
|
236
|
-
keeps its size (stochastic-per-epoch augmentation, backwards-compatible).
|
|
237
|
-
When ``> 0`` each batch becomes ``(1 + n_augmentation)`` times larger:
|
|
238
|
-
the clean originals are kept and ``n_augmentation`` independently
|
|
239
|
-
transformed copies are appended. This expresses augmentations defined as
|
|
240
|
-
a fixed set-expansion (e.g. the EEG-Inception MI 6x training set,
|
|
241
|
-
``n_augmentation=5``). In this mode batches are returned as ``(X, y)``.
|
|
242
201
|
**kwargs : dict, optional
|
|
243
202
|
keyword arguments to pass to standard DataLoader class.
|
|
244
203
|
"""
|
|
245
204
|
|
|
246
|
-
def __init__(
|
|
247
|
-
self, dataset, transforms=None, device=None, n_augmentation=0, **kwargs
|
|
248
|
-
):
|
|
205
|
+
def __init__(self, dataset, transforms=None, device=None, **kwargs):
|
|
249
206
|
if "collate_fn" in kwargs:
|
|
250
207
|
raise ValueError(
|
|
251
208
|
"collate_fn cannot be used in this context because it is used "
|
|
252
209
|
"to pass transform"
|
|
253
210
|
)
|
|
254
|
-
if n_augmentation < 0:
|
|
255
|
-
raise ValueError("n_augmentation must be a non-negative integer.")
|
|
256
211
|
if transforms is None or (
|
|
257
212
|
isinstance(transforms, list) and len(transforms) == 0
|
|
258
213
|
):
|
|
259
|
-
|
|
214
|
+
self.collated_tr = _make_collateable(IdentityTransform(), device=device)
|
|
260
215
|
elif isinstance(transforms, (Transform, nn.Module)):
|
|
261
|
-
|
|
216
|
+
self.collated_tr = _make_collateable(transforms, device=device)
|
|
262
217
|
elif isinstance(transforms, list):
|
|
263
|
-
|
|
218
|
+
self.collated_tr = _make_collateable(Compose(transforms), device=device)
|
|
264
219
|
else:
|
|
265
220
|
raise TypeError(
|
|
266
221
|
"transforms can be either a Transform object "
|
|
267
222
|
"or a list of Transform objects."
|
|
268
223
|
)
|
|
269
224
|
|
|
270
|
-
self.collated_tr = _AugmentationCollate(
|
|
271
|
-
transform, device=device, n_augmentation=n_augmentation
|
|
272
|
-
)
|
|
273
225
|
super().__init__(dataset, collate_fn=self.collated_tr, **kwargs)
|
|
@@ -18,7 +18,7 @@ from mne.filter import notch_filter
|
|
|
18
18
|
from scipy.interpolate import Rbf
|
|
19
19
|
from sklearn.utils import check_random_state
|
|
20
20
|
from torch.fft import fft, ifft
|
|
21
|
-
from torch.nn.functional import pad
|
|
21
|
+
from torch.nn.functional import one_hot, pad
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def identity(X: torch.Tensor, y: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
|
@@ -253,34 +253,25 @@ def channels_dropout(
|
|
|
253
253
|
return X * mask.unsqueeze(-1), y
|
|
254
254
|
|
|
255
255
|
|
|
256
|
-
def
|
|
256
|
+
def _make_permutation_matrix(
|
|
257
257
|
X: torch.Tensor, mask: torch.Tensor, random_state: int | np.random.Generator | None
|
|
258
258
|
) -> torch.Tensor:
|
|
259
|
-
"""Per-sample channel permutation that shuffles only the masked channels.
|
|
260
|
-
|
|
261
|
-
Returns a ``(batch, n_channels)`` index permutation: output channel ``i`` is
|
|
262
|
-
taken from input channel ``perm[b, i]``. Masked channels are permuted among
|
|
263
|
-
their own positions; unmasked channels keep their place.
|
|
264
|
-
|
|
265
|
-
The per-sample ``rng.permutation`` is kept (rather than a vectorized random
|
|
266
|
-
draw) so the output is bit-for-bit identical to the previous one-hot/matmul
|
|
267
|
-
implementation — vectorizing the draw would change seeded results. The win
|
|
268
|
-
is downstream: the caller gathers with this index instead of building
|
|
269
|
-
``(batch, n_channels, n_channels)`` one-hot matrices and a matmul.
|
|
270
|
-
"""
|
|
271
259
|
rng = check_random_state(random_state)
|
|
272
260
|
batch_size, n_channels, _ = X.shape
|
|
273
|
-
hard_mask = mask.round()
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
261
|
+
hard_mask = mask.round()
|
|
262
|
+
batch_permutations = torch.empty(
|
|
263
|
+
batch_size, n_channels, n_channels, device=X.device
|
|
264
|
+
)
|
|
265
|
+
for b, mask in enumerate(hard_mask):
|
|
266
|
+
channels_to_shuffle = torch.arange(n_channels, device=X.device)
|
|
267
|
+
channels_to_shuffle = channels_to_shuffle[mask.bool()]
|
|
268
|
+
reordered_channels = torch.tensor(
|
|
269
|
+
rng.permutation(channels_to_shuffle.cpu()), device=X.device
|
|
282
270
|
)
|
|
283
|
-
|
|
271
|
+
channels_permutation = torch.arange(n_channels, device=X.device)
|
|
272
|
+
channels_permutation[channels_to_shuffle] = reordered_channels
|
|
273
|
+
batch_permutations[b, ...] = one_hot(channels_permutation)
|
|
274
|
+
return batch_permutations
|
|
284
275
|
|
|
285
276
|
|
|
286
277
|
def channels_shuffle(
|
|
@@ -323,11 +314,8 @@ def channels_shuffle(
|
|
|
323
314
|
if p_shuffle == 0:
|
|
324
315
|
return X, y
|
|
325
316
|
mask = _pick_channels_randomly(X, 1 - p_shuffle, random_state)
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
# one-hot permutation matrices and a (batch, C, C) @ (batch, C, T) matmul.
|
|
329
|
-
perm = perm.unsqueeze(-1).expand(-1, -1, X.shape[-1])
|
|
330
|
-
return torch.gather(X, 1, perm), y
|
|
317
|
+
batch_permutations = _make_permutation_matrix(X, mask, random_state)
|
|
318
|
+
return torch.matmul(batch_permutations, X), y
|
|
331
319
|
|
|
332
320
|
|
|
333
321
|
def gaussian_noise(
|
|
@@ -1200,21 +1188,19 @@ def mask_encoding(
|
|
|
1200
1188
|
32 (2024): 875-886.
|
|
1201
1189
|
"""
|
|
1202
1190
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
)
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
return X.masked_fill(time_mask.unsqueeze(1), 0), y
|
|
1191
|
+
batch_indices = torch.arange(X.shape[0]).repeat_interleave(n_segments)
|
|
1192
|
+
start_indices = time_start.flatten()
|
|
1193
|
+
mask_indices = start_indices[:, None] + torch.arange(segment_length)
|
|
1194
|
+
|
|
1195
|
+
# Create a boolean mask with the same shape as X
|
|
1196
|
+
mask = torch.zeros_like(X, dtype=torch.bool)
|
|
1197
|
+
for batch_index, grouped_mask_indices in zip(batch_indices, mask_indices):
|
|
1198
|
+
mask[batch_index, :, grouped_mask_indices] = True
|
|
1199
|
+
|
|
1200
|
+
# Apply the mask to set the values to 0
|
|
1201
|
+
X[mask] = 0
|
|
1202
|
+
|
|
1203
|
+
return X, y # Return the masked tensor and labels
|
|
1218
1204
|
|
|
1219
1205
|
|
|
1220
1206
|
def channels_rereference(
|
|
@@ -10,7 +10,6 @@ from .base import (
|
|
|
10
10
|
from .bcicomp import BCICompetitionIVDataset4
|
|
11
11
|
from .bids import BIDSDataset, BIDSEpochsDataset
|
|
12
12
|
from .chb_mit import CHBMIT
|
|
13
|
-
from .collate import pad_channels_collate
|
|
14
13
|
from .mne import create_from_mne_epochs, create_from_mne_raw
|
|
15
14
|
from .moabb import BNCI2014_001, HGD, MOABBDataset
|
|
16
15
|
from .nmt import NMT
|
|
@@ -43,5 +42,4 @@ __all__ = [
|
|
|
43
42
|
"SleepPhysionetChallenge2018",
|
|
44
43
|
"create_from_X_y",
|
|
45
44
|
"BCICompetitionIVDataset4",
|
|
46
|
-
"pad_channels_collate",
|
|
47
45
|
]
|
|
@@ -192,31 +192,6 @@ def _channel_info(mne_obj):
|
|
|
192
192
|
return n_ch, type_str, sfreq
|
|
193
193
|
|
|
194
194
|
|
|
195
|
-
def _compute_ch_pos(info, targets_from):
|
|
196
|
-
"""Return ``(n_ch, 3)`` float32 electrode positions aligned to ``X``'s rows.
|
|
197
|
-
|
|
198
|
-
Positions are the MNE head-frame coordinates ``info["chs"][i]["loc"][:3]``
|
|
199
|
-
(meters) -- the same representation braindecode models already consume (see
|
|
200
|
-
e.g. ``signal_jepa``/``REVE``). When ``targets_from != "metadata"`` the
|
|
201
|
-
window's misc channels are dropped from ``X`` in ``__getitem__``, so the
|
|
202
|
-
matching misc rows are dropped here too. Emits a one-time ``UserWarning`` if
|
|
203
|
-
no channel carries a real position (no montage set).
|
|
204
|
-
"""
|
|
205
|
-
pos = np.asarray([ch["loc"][:3] for ch in info["chs"]], dtype="float32")
|
|
206
|
-
if targets_from != "metadata":
|
|
207
|
-
misc_mask = np.asarray(info.get_channel_types()) == "misc"
|
|
208
|
-
pos = pos[~misc_mask]
|
|
209
|
-
has_pos = np.isfinite(pos).all(axis=1) & np.any(pos != 0, axis=1)
|
|
210
|
-
if pos.size and not has_pos.any():
|
|
211
|
-
warnings.warn(
|
|
212
|
-
"Returning channel positions but no montage is set: all positions "
|
|
213
|
-
"are zero/non-finite. Call ``raw.set_montage(...)`` (or set a "
|
|
214
|
-
"montage during preprocessing) before enabling ``return_ch_pos``.",
|
|
215
|
-
UserWarning,
|
|
216
|
-
)
|
|
217
|
-
return pos
|
|
218
|
-
|
|
219
|
-
|
|
220
195
|
def _window_info(crop_inds, sfreq):
|
|
221
196
|
"""Extract window size from crop indices.
|
|
222
197
|
|
|
@@ -473,10 +448,6 @@ class RecordDataset(Dataset[tuple[np.ndarray, int | str, tuple[int, int, int]]])
|
|
|
473
448
|
):
|
|
474
449
|
self._description = _create_description(description)
|
|
475
450
|
self.transform = transform
|
|
476
|
-
# Opt-in: when True, ``__getitem__`` appends channel positions (n_ch, 3).
|
|
477
|
-
# Default off keeps the public ``(X, y, crop_inds)`` contract intact.
|
|
478
|
-
self.return_ch_pos = False
|
|
479
|
-
self._ch_pos = None
|
|
480
451
|
|
|
481
452
|
@abstractmethod
|
|
482
453
|
def __len__(self) -> int:
|
|
@@ -779,20 +750,6 @@ class EEGWindowsDataset(_ZarrMixin, RecordDataset):
|
|
|
779
750
|
@raw.setter
|
|
780
751
|
def raw(self, value):
|
|
781
752
|
self._raw = value
|
|
782
|
-
self._ch_pos = None # invalidate cached positions
|
|
783
|
-
|
|
784
|
-
@property
|
|
785
|
-
def ch_pos(self):
|
|
786
|
-
"""Electrode positions ``(n_ch, 3)`` (x, y, z) aligned to ``X``'s channels.
|
|
787
|
-
|
|
788
|
-
Cached MNE head-frame coordinates in meters. Rows match the channels
|
|
789
|
-
returned by :meth:`__getitem__` (misc target channels excluded when
|
|
790
|
-
``targets_from="channels"``). All-zero/NaN when no montage is set.
|
|
791
|
-
"""
|
|
792
|
-
if self._ch_pos is None:
|
|
793
|
-
info = self._raw.info if self._raw is not None else self._make_mne_info()
|
|
794
|
-
self._ch_pos = _compute_ch_pos(info, self.targets_from)
|
|
795
|
-
return self._ch_pos
|
|
796
753
|
|
|
797
754
|
def __getitem__(self, index: int):
|
|
798
755
|
"""Get a window and its target.
|
|
@@ -836,8 +793,6 @@ class EEGWindowsDataset(_ZarrMixin, RecordDataset):
|
|
|
836
793
|
y = X[misc_mask, :]
|
|
837
794
|
y = y.copy()
|
|
838
795
|
X = X[~misc_mask, :]
|
|
839
|
-
if self.return_ch_pos:
|
|
840
|
-
return X, y, crop_inds, self.ch_pos
|
|
841
796
|
return X, y, crop_inds
|
|
842
797
|
|
|
843
798
|
def __len__(self):
|
|
@@ -1061,24 +1016,6 @@ class WindowsDataset(_ZarrMixin, RecordDataset):
|
|
|
1061
1016
|
@windows.setter
|
|
1062
1017
|
def windows(self, value):
|
|
1063
1018
|
self._windows = value
|
|
1064
|
-
self._ch_pos = None # invalidate cached positions
|
|
1065
|
-
|
|
1066
|
-
@property
|
|
1067
|
-
def ch_pos(self):
|
|
1068
|
-
"""Electrode positions ``(n_ch, 3)`` (x, y, z) aligned to ``X``'s channels.
|
|
1069
|
-
|
|
1070
|
-
Cached MNE head-frame coordinates in meters. Rows match the channels
|
|
1071
|
-
returned by :meth:`__getitem__` (misc target channels excluded when
|
|
1072
|
-
``targets_from="channels"``). All-zero/NaN when no montage is set.
|
|
1073
|
-
"""
|
|
1074
|
-
if self._ch_pos is None:
|
|
1075
|
-
info = (
|
|
1076
|
-
self._windows.info
|
|
1077
|
-
if self._windows is not None
|
|
1078
|
-
else self._make_mne_info()
|
|
1079
|
-
)
|
|
1080
|
-
self._ch_pos = _compute_ch_pos(info, self.targets_from)
|
|
1081
|
-
return self._ch_pos
|
|
1082
1019
|
|
|
1083
1020
|
@staticmethod
|
|
1084
1021
|
def _can_use_fast_get_epoch_from_raw(epochs: mne.BaseEpochs) -> bool:
|
|
@@ -1131,8 +1068,6 @@ class WindowsDataset(_ZarrMixin, RecordDataset):
|
|
|
1131
1068
|
# necessary to cast as list to get list of three tensors from batch,
|
|
1132
1069
|
# otherwise get single 2d-tensor...
|
|
1133
1070
|
crop_inds = self.crop_inds[index].tolist()
|
|
1134
|
-
if self.return_ch_pos:
|
|
1135
|
-
return X, y, crop_inds, self.ch_pos
|
|
1136
1071
|
return X, y, crop_inds
|
|
1137
1072
|
|
|
1138
1073
|
def __len__(self) -> int:
|
|
@@ -1523,50 +1458,6 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1523
1458
|
)
|
|
1524
1459
|
return self
|
|
1525
1460
|
|
|
1526
|
-
def set_return_ch_pos(self, value: bool = True) -> "BaseConcatDataset":
|
|
1527
|
-
"""Toggle returning electrode positions from every subdataset.
|
|
1528
|
-
|
|
1529
|
-
When enabled, each windowed subdataset's ``__getitem__`` appends a
|
|
1530
|
-
``(n_ch, 3)`` array of electrode positions (x, y, z), so a batch yields
|
|
1531
|
-
``(X, y, crop_inds, ch_pos)``. Heterogeneous (variable-channel)
|
|
1532
|
-
collections additionally need
|
|
1533
|
-
:func:`braindecode.datasets.pad_channels_collate` as the DataLoader
|
|
1534
|
-
``collate_fn``. Default-off: leaves the ``(X, y, crop_inds)`` contract
|
|
1535
|
-
untouched.
|
|
1536
|
-
|
|
1537
|
-
Parameters
|
|
1538
|
-
----------
|
|
1539
|
-
value : bool
|
|
1540
|
-
Whether to return channel positions (default ``True``).
|
|
1541
|
-
|
|
1542
|
-
Returns
|
|
1543
|
-
-------
|
|
1544
|
-
self : BaseConcatDataset
|
|
1545
|
-
"""
|
|
1546
|
-
for ds in self.datasets:
|
|
1547
|
-
ds.return_ch_pos = value
|
|
1548
|
-
return self
|
|
1549
|
-
|
|
1550
|
-
@property
|
|
1551
|
-
def ch_pos(self):
|
|
1552
|
-
"""Electrode positions ``(n_ch, 3)`` from the first recording.
|
|
1553
|
-
|
|
1554
|
-
Convenience accessor mirroring how the repr reports channel info "from
|
|
1555
|
-
first recording". For per-sample positions in a heterogeneous
|
|
1556
|
-
collection, enable :meth:`set_return_ch_pos` and read them from the
|
|
1557
|
-
batch instead.
|
|
1558
|
-
"""
|
|
1559
|
-
if not self.datasets:
|
|
1560
|
-
raise ValueError("Empty BaseConcatDataset has no channel positions.")
|
|
1561
|
-
first = self.datasets[0]
|
|
1562
|
-
if not hasattr(first, "ch_pos"):
|
|
1563
|
-
raise AttributeError(
|
|
1564
|
-
f"{type(first).__name__} does not expose channel positions; "
|
|
1565
|
-
"ch_pos is available on windowed datasets (EEGWindowsDataset / "
|
|
1566
|
-
"WindowsDataset)."
|
|
1567
|
-
)
|
|
1568
|
-
return first.ch_pos
|
|
1569
|
-
|
|
1570
1461
|
def _outdated_save(self, path, overwrite=False):
|
|
1571
1462
|
"""This is a copy of the old saving function, that had inconsistent.
|
|
1572
1463
|
|
|
@@ -134,14 +134,7 @@ class _EEGNeuralNet(NeuralNet, abc.ABC):
|
|
|
134
134
|
i_window_in_trials = []
|
|
135
135
|
i_window_stops = []
|
|
136
136
|
window_ys = []
|
|
137
|
-
for
|
|
138
|
-
if len(batch) > 3:
|
|
139
|
-
raise ValueError(
|
|
140
|
-
"Cropped prediction does not support channel positions; "
|
|
141
|
-
"disable return_ch_pos (set_return_ch_pos(False)) for cropped "
|
|
142
|
-
"evaluation."
|
|
143
|
-
)
|
|
144
|
-
X, y, i = batch[0], batch[1], batch[2]
|
|
137
|
+
for X, y, i in self.get_iterator(dataset, drop_index=False):
|
|
145
138
|
i_window_in_trials.append(i[0].cpu().numpy())
|
|
146
139
|
i_window_stops.append(i[2].cpu().numpy())
|
|
147
140
|
with torch.no_grad():
|
|
@@ -1,30 +1,22 @@
|
|
|
1
1
|
from .functions import (
|
|
2
2
|
_get_gaussian_kernel1d,
|
|
3
|
-
daubechies_filters,
|
|
4
3
|
drop_path,
|
|
5
|
-
dwt_max_level,
|
|
6
4
|
hilbert_freq,
|
|
7
5
|
identity,
|
|
8
6
|
plv_time,
|
|
9
7
|
safe_log,
|
|
10
|
-
sinusoidal_positional_encoding,
|
|
11
8
|
square,
|
|
12
|
-
wavelet_decomposition,
|
|
13
9
|
)
|
|
14
10
|
from .initialization import glorot_weight_zero_bias, rescale_parameter
|
|
15
11
|
|
|
16
12
|
__all__ = [
|
|
17
13
|
"_get_gaussian_kernel1d",
|
|
18
|
-
"daubechies_filters",
|
|
19
14
|
"drop_path",
|
|
20
|
-
"dwt_max_level",
|
|
21
15
|
"hilbert_freq",
|
|
22
16
|
"identity",
|
|
23
17
|
"plv_time",
|
|
24
18
|
"safe_log",
|
|
25
|
-
"sinusoidal_positional_encoding",
|
|
26
19
|
"square",
|
|
27
|
-
"wavelet_decomposition",
|
|
28
20
|
"glorot_weight_zero_bias",
|
|
29
21
|
"rescale_parameter",
|
|
30
22
|
]
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
# Authors: Robin Schirrmeister <robintibor@gmail.com>
|
|
2
|
-
# Bruno Aristimunha <b.aristimunha@gmail.com>
|
|
3
2
|
#
|
|
4
3
|
# License: BSD (3-clause)
|
|
5
4
|
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
import math
|
|
9
|
-
|
|
10
|
-
import numpy as np
|
|
11
5
|
import torch
|
|
12
6
|
import torch.nn.functional as F
|
|
13
7
|
|
|
@@ -255,156 +249,3 @@ def plv_time(x, forward_fourier=True, epsilon: float = 1e-6):
|
|
|
255
249
|
)
|
|
256
250
|
|
|
257
251
|
return plv_matrix
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def daubechies_filters(n_vanishing: int) -> torch.Tensor:
|
|
261
|
-
r"""Daubechies ``db<n_vanishing>`` wavelet decomposition filters.
|
|
262
|
-
|
|
263
|
-
Computes the orthogonal decomposition (analysis) low-pass and high-pass
|
|
264
|
-
filters by spectral factorisation of the Daubechies half-band (Bezout)
|
|
265
|
-
polynomial, so no external wavelet library (PyWavelets, ptwt) is required.
|
|
266
|
-
For ``n_vanishing=4`` the result equals ``pywt.Wavelet("db4").dec_lo`` /
|
|
267
|
-
``.dec_hi`` to machine precision (bit-identical once cast to ``float32``).
|
|
268
|
-
|
|
269
|
-
Parameters
|
|
270
|
-
----------
|
|
271
|
-
n_vanishing : int
|
|
272
|
-
Number of vanishing moments (the ``N`` in ``dbN``). The filters have
|
|
273
|
-
``2 * n_vanishing`` taps.
|
|
274
|
-
|
|
275
|
-
Returns
|
|
276
|
-
-------
|
|
277
|
-
torch.Tensor
|
|
278
|
-
Shape ``(2, 2 * n_vanishing)`` whose rows are the low-pass and high-pass
|
|
279
|
-
decomposition filters ``[dec_lo, dec_hi]``.
|
|
280
|
-
|
|
281
|
-
Notes
|
|
282
|
-
-----
|
|
283
|
-
Half-band polynomial :math:`P(y) = \sum_{k=0}^{N-1} \binom{N-1+k}{k} y^k`
|
|
284
|
-
with :math:`y = (1 - \cos\omega)/2`. Its roots are mapped to the
|
|
285
|
-
minimum-phase (:math:`|z| < 1`) roots of :math:`z^2 - 2(1-2y)z + 1`; the
|
|
286
|
-
analysis low-pass is :math:`H(z) = (1+z)^N` times that spectral factor
|
|
287
|
-
(normalised so it sums to :math:`\sqrt 2`), and the high-pass is the
|
|
288
|
-
quadrature mirror :math:`g_k = (-1)^{k+1} h_{L-1-k}`.
|
|
289
|
-
"""
|
|
290
|
-
n = n_vanishing
|
|
291
|
-
poly = np.array([math.comb(n - 1 + k, k) for k in range(n)], dtype=float)
|
|
292
|
-
z_inside = []
|
|
293
|
-
for y in np.roots(poly[::-1]):
|
|
294
|
-
b = -2.0 * (1.0 - 2.0 * y)
|
|
295
|
-
disc = np.sqrt(b * b - 4.0 + 0j)
|
|
296
|
-
r1, r2 = (-b + disc) / 2.0, (-b - disc) / 2.0
|
|
297
|
-
z_inside.append(r1 if abs(r1) < 1.0 else r2)
|
|
298
|
-
low = np.poly([-1.0] * n + z_inside).real
|
|
299
|
-
low *= math.sqrt(2.0) / low.sum()
|
|
300
|
-
dec_lo = low[::-1] # pywt stores the time-reversed decomposition low-pass
|
|
301
|
-
length = len(dec_lo)
|
|
302
|
-
dec_hi = np.array(
|
|
303
|
-
[(-1.0) ** (k + 1) * dec_lo[length - 1 - k] for k in range(length)]
|
|
304
|
-
)
|
|
305
|
-
return torch.tensor(np.stack([dec_lo, dec_hi]), dtype=torch.float32)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
def dwt_max_level(n_times: int, filter_len: int) -> int:
|
|
309
|
-
"""Maximum number of discrete wavelet decomposition levels.
|
|
310
|
-
|
|
311
|
-
Matches ``pywt.dwt_max_level`` = ``floor(log2(n_times / (filter_len - 1)))``.
|
|
312
|
-
|
|
313
|
-
Parameters
|
|
314
|
-
----------
|
|
315
|
-
n_times : int
|
|
316
|
-
Length of the signal.
|
|
317
|
-
filter_len : int
|
|
318
|
-
Number of wavelet filter taps.
|
|
319
|
-
|
|
320
|
-
Returns
|
|
321
|
-
-------
|
|
322
|
-
int
|
|
323
|
-
Maximum decomposition level (``0`` if the signal is shorter than the
|
|
324
|
-
filter).
|
|
325
|
-
"""
|
|
326
|
-
if n_times < filter_len:
|
|
327
|
-
return 0
|
|
328
|
-
return max(0, int(math.floor(math.log2(n_times / (filter_len - 1)))))
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
def wavelet_decomposition(
|
|
332
|
-
x: torch.Tensor, filters: torch.Tensor, n_levels: int | None = None
|
|
333
|
-
) -> torch.Tensor:
|
|
334
|
-
r"""Multilevel discrete wavelet decomposition with periodic boundary.
|
|
335
|
-
|
|
336
|
-
A cascade of strided convolutions with circular padding, bit-identical to
|
|
337
|
-
``pywt`` / ``ptwt`` ``wavedec(mode="periodic")``. The approximation and
|
|
338
|
-
detail coefficients of every level are concatenated along the last axis as
|
|
339
|
-
``[cA_n, cD_n, cD_{n-1}, ..., cD_1]``.
|
|
340
|
-
|
|
341
|
-
Parameters
|
|
342
|
-
----------
|
|
343
|
-
x : torch.Tensor
|
|
344
|
-
Input signal of shape ``(..., n_times)``.
|
|
345
|
-
filters : torch.Tensor
|
|
346
|
-
Decomposition filters of shape ``(2, filter_len)`` =
|
|
347
|
-
``[low-pass, high-pass]`` (e.g. from :func:`daubechies_filters`).
|
|
348
|
-
n_levels : int, optional
|
|
349
|
-
Number of decomposition levels. Defaults to the maximum
|
|
350
|
-
(:func:`dwt_max_level`).
|
|
351
|
-
|
|
352
|
-
Returns
|
|
353
|
-
-------
|
|
354
|
-
torch.Tensor
|
|
355
|
-
Concatenated coefficients of shape ``(..., dwt_len)``.
|
|
356
|
-
"""
|
|
357
|
-
filter_len = filters.shape[-1]
|
|
358
|
-
if n_levels is None:
|
|
359
|
-
n_levels = dwt_max_level(x.shape[-1], filter_len)
|
|
360
|
-
leading = x.shape[:-1]
|
|
361
|
-
approx = x.reshape(-1, x.shape[-1])
|
|
362
|
-
# Flip the taps so conv1d (cross-correlation) realises the wavelet convolution.
|
|
363
|
-
weight = filters.flip(-1).unsqueeze(1).to(device=approx.device, dtype=approx.dtype)
|
|
364
|
-
details = []
|
|
365
|
-
for _ in range(n_levels):
|
|
366
|
-
pad = (2 * filter_len - 3) // 2
|
|
367
|
-
padded = F.pad(
|
|
368
|
-
approx.unsqueeze(1), (pad, pad + approx.shape[-1] % 2), mode="circular"
|
|
369
|
-
)
|
|
370
|
-
approx, detail = F.conv1d(padded, weight, stride=2).unbind(dim=1)
|
|
371
|
-
details.append(detail)
|
|
372
|
-
out = torch.cat([approx, *reversed(details)], dim=-1)
|
|
373
|
-
return out.reshape(*leading, -1)
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
def sinusoidal_positional_encoding(n_positions: int, dim: int) -> torch.Tensor:
|
|
377
|
-
r"""Fixed sine/cosine positional-encoding table of shape ``(n_positions, dim)``.
|
|
378
|
-
|
|
379
|
-
The standard encoding of Vaswani et al. (2017): for position :math:`p` and
|
|
380
|
-
channel :math:`i`, :math:`pe[p, 2i] = \sin(p / 10000^{2i/d})` and
|
|
381
|
-
:math:`pe[p, 2i+1] = \cos(p / 10000^{2i/d})`. PyTorch ships no sinusoidal
|
|
382
|
-
encoding (``nn.Embedding`` is a *learned* lookup), so braindecode models that
|
|
383
|
-
need one share this primitive instead of re-deriving it. An odd ``dim`` is
|
|
384
|
-
computed on the next even width and truncated, reproducing the per-model
|
|
385
|
-
wrappers (e.g. :class:`~braindecode.models.medformer` odd-``d_model``).
|
|
386
|
-
|
|
387
|
-
Parameters
|
|
388
|
-
----------
|
|
389
|
-
n_positions : int
|
|
390
|
-
Number of positions (sequence length) to encode.
|
|
391
|
-
dim : int
|
|
392
|
-
Embedding dimension of each position.
|
|
393
|
-
|
|
394
|
-
Returns
|
|
395
|
-
-------
|
|
396
|
-
torch.Tensor
|
|
397
|
-
``(n_positions, dim)`` float table; callers add their own batch axis,
|
|
398
|
-
dropout, or offset.
|
|
399
|
-
"""
|
|
400
|
-
dim_even = dim + (dim % 2)
|
|
401
|
-
position = torch.arange(n_positions).unsqueeze(1).float()
|
|
402
|
-
div_term = torch.exp(
|
|
403
|
-
torch.arange(0, dim_even, 2).float() * (-math.log(10000.0) / dim_even)
|
|
404
|
-
)
|
|
405
|
-
pe = torch.zeros(n_positions, dim_even)
|
|
406
|
-
pe[:, 0::2] = torch.sin(position * div_term)
|
|
407
|
-
pe[:, 1::2] = torch.cos(position * div_term)
|
|
408
|
-
# ``.contiguous()`` so an odd-``dim`` truncation owns tight storage -- a
|
|
409
|
-
# non-contiguous view over the padded width breaks safetensors buffer saving.
|
|
410
|
-
return pe[:, :dim].contiguous()
|
|
@@ -38,7 +38,6 @@ from .luna import LUNA
|
|
|
38
38
|
from .medformer import MEDFormer
|
|
39
39
|
from .meta_neuromotor import MetaNeuromotorHand
|
|
40
40
|
from .msvtnet import MSVTNet
|
|
41
|
-
from .mvpformer import MVPFormer
|
|
42
41
|
from .patchedtransformer import PBT
|
|
43
42
|
from .reve import REVE
|
|
44
43
|
from .sccnet import SCCNet
|
|
@@ -55,7 +54,6 @@ from .sleep_stager_blanco_2020 import SleepStagerBlanco2020
|
|
|
55
54
|
from .sleep_stager_chambon_2018 import SleepStagerChambon2018
|
|
56
55
|
from .sparcnet import SPARCNet
|
|
57
56
|
from .sstdpn import SSTDPN
|
|
58
|
-
from .steegformer import STEEGFormer
|
|
59
57
|
from .syncnet import SyncNet
|
|
60
58
|
from .tcformer import TCFormer
|
|
61
59
|
from .tcn import BDTCN, TCN
|
|
@@ -67,7 +65,6 @@ from .util import (
|
|
|
67
65
|
build_model_config,
|
|
68
66
|
extract_channel_locations_from_chs_info,
|
|
69
67
|
models_mandatory_parameters,
|
|
70
|
-
positions_from_chs_info,
|
|
71
68
|
)
|
|
72
69
|
|
|
73
70
|
# Call this last in order to make sure the dataset list is populated with
|
|
@@ -117,10 +114,8 @@ __all__ = [
|
|
|
117
114
|
"Labram",
|
|
118
115
|
"LUNA",
|
|
119
116
|
"extract_channel_locations_from_chs_info",
|
|
120
|
-
"positions_from_chs_info",
|
|
121
117
|
"MEDFormer",
|
|
122
118
|
"MSVTNet",
|
|
123
|
-
"MVPFormer",
|
|
124
119
|
"PBT",
|
|
125
120
|
"REVE",
|
|
126
121
|
"SCCNet",
|
|
@@ -134,7 +129,6 @@ __all__ = [
|
|
|
134
129
|
"SleepStagerBlanco2020",
|
|
135
130
|
"SleepStagerChambon2018",
|
|
136
131
|
"SPARCNet",
|
|
137
|
-
"STEEGFormer",
|
|
138
132
|
"SyncNet",
|
|
139
133
|
"BDTCN",
|
|
140
134
|
"TCFormer",
|