braindecode 1.8.1.dev169484576__tar.gz → 1.8.1.dev169575464__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.8.1.dev169484576/braindecode.egg-info → braindecode-1.8.1.dev169575464}/PKG-INFO +4 -3
- braindecode-1.8.1.dev169575464/braindecode/__init__.py +53 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/augmentation/base.py +99 -3
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/augmentation/transforms.py +2 -1
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/classifier.py +13 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/base.py +90 -9
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bids/datasets.py +32 -8
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datautil/serialization.py +15 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datautil/util.py +3 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/eegneuralnet.py +50 -8
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/functional/__init__.py +15 -0
- braindecode-1.8.1.dev169575464/braindecode/functional/_lfilter.py +118 -0
- braindecode-1.8.1.dev169575464/braindecode/functional/channel_mask.py +214 -0
- braindecode-1.8.1.dev169575464/braindecode/functional/patches.py +105 -0
- braindecode-1.8.1.dev169575464/braindecode/models/__init__.py +133 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/atcnet.py +5 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/axon.py +5 -3
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/barista.py +80 -35
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/base.py +77 -14
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/bendr.py +4 -6
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/biot.py +12 -58
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/brainbert.py +41 -32
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/brainmodule.py +1 -1
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/brant.py +58 -37
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/codebrain.py +1 -1
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/config.py +10 -6
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/csbrain.py +3 -3
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/ctnet.py +42 -150
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/diver1.py +143 -33
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/duin.py +41 -6
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegconformer.py +86 -76
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegdino.py +17 -74
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eeginception_erp.py +12 -5
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eeginception_mi.py +7 -3
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegpt.py +19 -215
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/emg2qwerty.py +6 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/epint.py +39 -21
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/labram.py +2 -6
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/luna.py +25 -52
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/medformer.py +11 -20
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/meta_neuromotor.py +6 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/mirepnet.py +27 -79
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/mscformer.py +42 -161
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/mvpformer.py +79 -11
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/neurorvq.py +39 -61
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/neurorvq_tokenizer.py +5 -5
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/patchedtransformer.py +2 -3
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/popt.py +13 -11
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/reve.py +10 -10
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/signal_jepa.py +7 -6
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/sleepfm.py +3 -3
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/util.py +129 -34
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/zuna.py +115 -4
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/__init__.py +12 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/attention.py +101 -5
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/blocks.py +2 -61
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/dance_modules.py +5 -3
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/filter.py +6 -8
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/layers.py +187 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/parametrization.py +10 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/preprocessing/eegprep_preprocess.py +61 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/preprocessing/mne_preprocess.py +79 -4
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/preprocessing/preprocess.py +34 -24
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/preprocessing/windowers.py +9 -8
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/regressor.py +7 -1
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/samplers/base.py +27 -17
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/training/__init__.py +6 -0
- braindecode-1.8.1.dev169575464/braindecode/training/_trial_aggregation.py +106 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/training/scoring.py +335 -122
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/util.py +15 -10
- braindecode-1.8.1.dev169575464/braindecode/version.py +1 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/visualization/frequency.py +11 -7
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464/braindecode.egg-info}/PKG-INFO +4 -3
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode.egg-info/SOURCES.txt +6 -1
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode.egg-info/requires.txt +3 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/api.rst +18 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/index.rst +1 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/install/install.rst +1 -1
- braindecode-1.8.1.dev169575464/docs/user_guide/cropped_inference.rst +130 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/whats_new.rst +156 -2
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/pyproject.toml +8 -2
- braindecode-1.8.1.dev169484576/braindecode/__init__.py +0 -9
- braindecode-1.8.1.dev169484576/braindecode/models/__init__.py +0 -197
- braindecode-1.8.1.dev169484576/braindecode/version.py +0 -1
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/LICENSE.txt +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/MANIFEST.in +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/NOTICE.txt +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/README.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/augmentation/functional.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/__init__.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/_notebook_viewer.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bids/hub.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/collate.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/functional/_real_dft.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/functional/attention.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/functional/functions.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/brainomni.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/contrawr.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/dance.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/deep4.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eeg_clip.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegitnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegminer.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegnex.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegsym.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/fbmsnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/gcnsnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/guetschel2026.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/mapa.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/msvtnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/neuropose.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/seizure_transformer.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/sensingdynamics.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/shallow_fbcsp.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/sinc_shallow.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/steegformer.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/summary.csv +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/tcformer.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/tcn.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/tfm_tokenizer.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/tmsanet.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/usleep.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/models/vemg2pose.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/activation.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/channels.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/convolution.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/linear.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/linear_attention.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/quantization.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/stats.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/util.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/training/losses.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/visualization/activations.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/visualization/attribution.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/visualization/metrics.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/visualization/sanity.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/visualization/topology.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/Makefile +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/agent_docs.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/cite.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/conf.py +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/help.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/install/install_pip.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/install/install_source.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/models.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/models_table.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/docs/user_guide/channel_strategies.rst +0 -0
- {braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/setup.cfg +0 -0
{braindecode-1.8.1.dev169484576/braindecode.egg-info → braindecode-1.8.1.dev169575464}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: braindecode
|
|
3
|
-
Version: 1.8.1.
|
|
3
|
+
Version: 1.8.1.dev169575464
|
|
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>
|
|
@@ -22,8 +22,8 @@ Requires-Python: >=3.11
|
|
|
22
22
|
Description-Content-Type: text/x-rst
|
|
23
23
|
License-File: LICENSE.txt
|
|
24
24
|
License-File: NOTICE.txt
|
|
25
|
-
Requires-Dist: torch>=2.
|
|
26
|
-
Requires-Dist: torchaudio>=2.
|
|
25
|
+
Requires-Dist: torch>=2.6
|
|
26
|
+
Requires-Dist: torchaudio>=2.6
|
|
27
27
|
Requires-Dist: mne>=1.11.0
|
|
28
28
|
Requires-Dist: mne_bids>=0.18
|
|
29
29
|
Requires-Dist: h5py
|
|
@@ -50,6 +50,7 @@ Provides-Extra: tests
|
|
|
50
50
|
Requires-Dist: pytest<9.1; extra == "tests"
|
|
51
51
|
Requires-Dist: pytest-cov; extra == "tests"
|
|
52
52
|
Requires-Dist: pytest-xdist>=3.8.0; extra == "tests"
|
|
53
|
+
Requires-Dist: pytest-timeout; extra == "tests"
|
|
53
54
|
Requires-Dist: codecov; extra == "tests"
|
|
54
55
|
Requires-Dist: pytest_cases; extra == "tests"
|
|
55
56
|
Requires-Dist: mypy; extra == "tests"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from .version import __version__
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING: # type checkers and IDEs see the classes; runtime uses __getattr__
|
|
7
|
+
from .classifier import EEGClassifier
|
|
8
|
+
from .regressor import EEGRegressor
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"__version__",
|
|
12
|
+
"EEGClassifier",
|
|
13
|
+
"EEGRegressor",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
# Subpackages reachable as ``braindecode.<name>`` after a bare ``import braindecode``.
|
|
17
|
+
_SUBMODULES = frozenset(
|
|
18
|
+
{
|
|
19
|
+
"augmentation",
|
|
20
|
+
"classifier",
|
|
21
|
+
"datasets",
|
|
22
|
+
"datautil",
|
|
23
|
+
"eegneuralnet",
|
|
24
|
+
"functional",
|
|
25
|
+
"models",
|
|
26
|
+
"modules",
|
|
27
|
+
"preprocessing",
|
|
28
|
+
"regressor",
|
|
29
|
+
"samplers",
|
|
30
|
+
"training",
|
|
31
|
+
"util",
|
|
32
|
+
"visualization",
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if not TYPE_CHECKING: # hidden from checkers so misspelled names stay errors
|
|
37
|
+
|
|
38
|
+
def __getattr__(name):
|
|
39
|
+
# PEP 562: skorch, the datasets and the models load on first use.
|
|
40
|
+
if name == "EEGClassifier":
|
|
41
|
+
from .classifier import EEGClassifier
|
|
42
|
+
|
|
43
|
+
return EEGClassifier
|
|
44
|
+
if name == "EEGRegressor":
|
|
45
|
+
from .regressor import EEGRegressor
|
|
46
|
+
|
|
47
|
+
return EEGRegressor
|
|
48
|
+
if name in _SUBMODULES:
|
|
49
|
+
return importlib.import_module(f".{name}", __name__)
|
|
50
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
51
|
+
|
|
52
|
+
def __dir__():
|
|
53
|
+
return sorted(set(globals()) | set(__all__) | _SUBMODULES)
|
{braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/augmentation/base.py
RENAMED
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
from numbers import Real
|
|
10
10
|
from typing import Any, Callable, Optional, Union
|
|
11
11
|
|
|
12
|
+
import numpy as np
|
|
12
13
|
import torch
|
|
13
14
|
from sklearn.utils import check_random_state
|
|
14
15
|
from torch import Tensor, nn
|
|
15
|
-
from torch.utils.data import DataLoader
|
|
16
|
+
from torch.utils.data import DataLoader, get_worker_info
|
|
16
17
|
from torch.utils.data._utils.collate import default_collate
|
|
17
18
|
|
|
18
19
|
from .functional import identity
|
|
@@ -191,6 +192,59 @@ def _as_mixed_target(y, lam_dtype):
|
|
|
191
192
|
return y, y, lam
|
|
192
193
|
|
|
193
194
|
|
|
195
|
+
def _transform_rngs(transform):
|
|
196
|
+
"""Return the ``RandomState`` objects of a transform, in a fixed order.
|
|
197
|
+
|
|
198
|
+
Walks the transform, its submodules and the ``transforms`` of a
|
|
199
|
+
:class:`Compose`, and lists each ``rng`` once (pooled transforms of
|
|
200
|
+
:class:`braindecode.augmentation.TrivialAugment` share theirs). The global
|
|
201
|
+
numpy generator, used with ``random_state=None``, is included: a spawned
|
|
202
|
+
worker receives a pickled copy of it, which torch does not reseed.
|
|
203
|
+
"""
|
|
204
|
+
rngs, seen, stack = [], set(), [transform]
|
|
205
|
+
while stack:
|
|
206
|
+
tr = stack.pop(0)
|
|
207
|
+
if id(tr) in seen:
|
|
208
|
+
continue
|
|
209
|
+
seen.add(id(tr))
|
|
210
|
+
# Compose forwards to its children without using its inherited RNG.
|
|
211
|
+
rng = None if type(tr) is Compose else getattr(tr, "rng", None)
|
|
212
|
+
if isinstance(rng, np.random.RandomState) and id(rng) not in seen:
|
|
213
|
+
seen.add(id(rng))
|
|
214
|
+
rngs.append(rng)
|
|
215
|
+
children = list(getattr(tr, "transforms", None) or [])
|
|
216
|
+
if isinstance(tr, nn.Module):
|
|
217
|
+
children += list(tr.children())
|
|
218
|
+
stack.extend(children)
|
|
219
|
+
return rngs
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _assert_stackable(items, name):
|
|
223
|
+
# Fixed expansion concatenates the clean originals with the transformed
|
|
224
|
+
# copies, so every copy must keep the original's shape except along the
|
|
225
|
+
# batch dimension. A transform that reshapes X or y (e.g. one returning
|
|
226
|
+
# class distributions instead of class indices) would otherwise fail deep
|
|
227
|
+
# inside torch.cat with a bare dimension-mismatch RuntimeError.
|
|
228
|
+
reference = items[0]
|
|
229
|
+
for item in items[1:]:
|
|
230
|
+
pairs = (
|
|
231
|
+
zip(reference, item)
|
|
232
|
+
if isinstance(reference, (tuple, list))
|
|
233
|
+
else [(reference, item)]
|
|
234
|
+
)
|
|
235
|
+
for ref_part, part in pairs:
|
|
236
|
+
if part.dim() != ref_part.dim() or part.shape[1:] != ref_part.shape[1:]:
|
|
237
|
+
raise ValueError(
|
|
238
|
+
f"The transform returned {name} with shape "
|
|
239
|
+
f"{tuple(part.shape)} instead of {tuple(ref_part.shape)}: "
|
|
240
|
+
f"the fixed-expansion mode (n_augmentation > 0) keeps the "
|
|
241
|
+
f"clean originals and concatenates the transformed "
|
|
242
|
+
f"copies, so it cannot accommodate a transform that "
|
|
243
|
+
f"changes the shape of {name}. Use n_augmentation = 0 "
|
|
244
|
+
f"(in-place augmentation) instead."
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
194
248
|
class _AugmentationCollate:
|
|
195
249
|
"""Collate that applies a transform to each batch, with optional expansion.
|
|
196
250
|
|
|
@@ -211,15 +265,44 @@ class _AugmentationCollate:
|
|
|
211
265
|
``(1 + n_augmentation)`` times the original size. When the transform
|
|
212
266
|
mixes targets, as :class:`braindecode.augmentation.Mixup` does, ``y``
|
|
213
267
|
stays the ``(y_a, y_b, lam)`` triple and the clean originals get a
|
|
214
|
-
mixing coefficient of one.
|
|
268
|
+
mixing coefficient of one. In this mode a transform that changes the
|
|
269
|
+
shape of ``X`` or ``y`` is rejected with a clear
|
|
270
|
+
:class:`ValueError`, as its copies cannot be concatenated with the
|
|
271
|
+
clean originals.
|
|
215
272
|
"""
|
|
216
273
|
|
|
217
274
|
def __init__(self, transform, device=None, n_augmentation=0):
|
|
218
275
|
self.transform = transform
|
|
219
276
|
self.device = device
|
|
220
277
|
self.n_augmentation = n_augmentation
|
|
278
|
+
self.worker_seeds = None
|
|
279
|
+
self._worker_rngs_seeded = False
|
|
280
|
+
|
|
281
|
+
def draw_worker_seeds(self):
|
|
282
|
+
"""Draw, from each generator of the transform, a seed for the workers.
|
|
283
|
+
|
|
284
|
+
Called in the main process when a multi-process iteration starts. A
|
|
285
|
+
worker receives a copy of the transform whose generators are in the
|
|
286
|
+
state of the main process; without a new seed, every worker, and every
|
|
287
|
+
epoch, would draw the same augmentations.
|
|
288
|
+
"""
|
|
289
|
+
self.worker_seeds = [
|
|
290
|
+
int(rng.randint(2**31)) for rng in _transform_rngs(self.transform)
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
def _seed_worker_rngs(self):
|
|
294
|
+
if self._worker_rngs_seeded or self.worker_seeds is None:
|
|
295
|
+
return
|
|
296
|
+
worker_info = get_worker_info()
|
|
297
|
+
if worker_info is None:
|
|
298
|
+
return
|
|
299
|
+
self._worker_rngs_seeded = True
|
|
300
|
+
rngs = _transform_rngs(self.transform)
|
|
301
|
+
for rng, seed in zip(rngs, self.worker_seeds):
|
|
302
|
+
rng.seed(np.random.SeedSequence([seed, worker_info.id]).generate_state(1))
|
|
221
303
|
|
|
222
304
|
def __call__(self, batch):
|
|
305
|
+
self._seed_worker_rngs()
|
|
223
306
|
collated = default_collate(batch)
|
|
224
307
|
X, y = collated[:2]
|
|
225
308
|
if self.device is not None:
|
|
@@ -239,13 +322,16 @@ class _AugmentationCollate:
|
|
|
239
322
|
for aug_y in ys
|
|
240
323
|
if isinstance(aug_y, (tuple, list)) and len(aug_y) == 3
|
|
241
324
|
]
|
|
325
|
+
_assert_stackable(xs, "X")
|
|
242
326
|
if mixed_ys:
|
|
243
327
|
# a target-mixing transform such as Mixup returns (y_a, y_b, lam),
|
|
244
328
|
# so the parts are concatenated one by one and the untouched copies
|
|
245
329
|
# are given a mixing coefficient of one
|
|
246
330
|
lam_dtype = mixed_ys[0][2].dtype
|
|
247
331
|
ys = [_as_mixed_target(aug_y, lam_dtype) for aug_y in ys]
|
|
332
|
+
_assert_stackable(ys, "y")
|
|
248
333
|
return torch.cat(xs), tuple(torch.cat(part) for part in zip(*ys))
|
|
334
|
+
_assert_stackable(ys, "y")
|
|
249
335
|
return torch.cat(xs), torch.cat(ys)
|
|
250
336
|
|
|
251
337
|
|
|
@@ -268,7 +354,10 @@ class AugmentedDataLoader(DataLoader):
|
|
|
268
354
|
the clean originals are kept and ``n_augmentation`` independently
|
|
269
355
|
transformed copies are appended. This expresses augmentations defined as
|
|
270
356
|
a fixed set-expansion (e.g. the EEG-Inception MI 6x training set,
|
|
271
|
-
``n_augmentation=5``). In this mode batches are returned as ``(X, y)
|
|
357
|
+
``n_augmentation=5``). In this mode batches are returned as ``(X, y)``
|
|
358
|
+
and transforms that change the shape of ``X`` or ``y`` are rejected
|
|
359
|
+
with a :class:`ValueError`, as their copies cannot be concatenated
|
|
360
|
+
with the clean originals.
|
|
272
361
|
**kwargs : dict, optional
|
|
273
362
|
keyword arguments to pass to standard DataLoader class.
|
|
274
363
|
"""
|
|
@@ -301,3 +390,10 @@ class AugmentedDataLoader(DataLoader):
|
|
|
301
390
|
transform, device=device, n_augmentation=n_augmentation
|
|
302
391
|
)
|
|
303
392
|
super().__init__(dataset, collate_fn=self.collated_tr, **kwargs)
|
|
393
|
+
|
|
394
|
+
def __iter__(self):
|
|
395
|
+
# the workers get a copy of the transform; give them new seeds so that
|
|
396
|
+
# seeded transforms differ across workers and epochs
|
|
397
|
+
if self.num_workers > 0:
|
|
398
|
+
self.collated_tr.draw_worker_seeds()
|
|
399
|
+
return super().__iter__()
|
|
@@ -1173,7 +1173,8 @@ class LabelSmoothing(Transform):
|
|
|
1173
1173
|
fixed-expansion mode (``n_augmentation > 0``) of
|
|
1174
1174
|
:class:`braindecode.augmentation.AugmentedDataLoader` is not supported:
|
|
1175
1175
|
it cannot concatenate the unsmoothed original targets with the reshaped
|
|
1176
|
-
smoothed copies
|
|
1176
|
+
smoothed copies and rejects the combination with a :class:`ValueError`
|
|
1177
|
+
pointing to ``n_augmentation = 0``.
|
|
1177
1178
|
|
|
1178
1179
|
Parameters
|
|
1179
1180
|
----------
|
|
@@ -33,7 +33,12 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
|
|
|
33
33
|
|
|
34
34
|
cropped: bool (default=False)
|
|
35
35
|
Defines whether torch model passed to this class is cropped or not.
|
|
36
|
-
Currently used for callbacks definition.
|
|
36
|
+
Currently used for callbacks definition. For input without window
|
|
37
|
+
indices (such as numpy arrays), scoring callbacks treat each example
|
|
38
|
+
as a separate trial and average its temporal predictions. To aggregate
|
|
39
|
+
multiple windows of the same trial, pass a window dataset retaining
|
|
40
|
+
its window indices. Cross-validation splits must keep windows from
|
|
41
|
+
the same trial together.
|
|
37
42
|
|
|
38
43
|
callbacks: None or list of strings or list of Callback instances (default=None)
|
|
39
44
|
More callbacks, in addition to those returned by
|
|
@@ -195,7 +200,7 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
|
|
|
195
200
|
"""
|
|
196
201
|
return self.predict_proba(X).argmax(1)
|
|
197
202
|
|
|
198
|
-
def predict_trials(self, X, return_targets=True):
|
|
203
|
+
def predict_trials(self, X, return_targets=True, *, loader_kwargs=None):
|
|
199
204
|
"""Create trialwise predictions from a cropped dataset.
|
|
200
205
|
|
|
201
206
|
Parameters
|
|
@@ -204,6 +209,9 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
|
|
|
204
209
|
A braindecode dataset to be predicted.
|
|
205
210
|
return_targets : bool
|
|
206
211
|
If True, additionally returns the trial targets.
|
|
212
|
+
loader_kwargs : dict | None
|
|
213
|
+
Cropped mode only: DataLoader overrides, see
|
|
214
|
+
:func:`braindecode.training.iter_trial_predictions`.
|
|
207
215
|
|
|
208
216
|
Returns
|
|
209
217
|
-------
|
|
@@ -220,6 +228,8 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
|
|
|
220
228
|
trial lengths differ, a list with one target array per trial.
|
|
221
229
|
"""
|
|
222
230
|
if not self.cropped:
|
|
231
|
+
if loader_kwargs is not None:
|
|
232
|
+
raise ValueError("loader_kwargs requires cropped=True")
|
|
223
233
|
warnings.warn(
|
|
224
234
|
"This method was designed to predict trials in cropped mode. "
|
|
225
235
|
"Calling it when cropped is False will give the same result as "
|
|
@@ -237,6 +247,7 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
|
|
|
237
247
|
return_targets=return_targets,
|
|
238
248
|
batch_size=self.batch_size,
|
|
239
249
|
num_workers=self.get_iterator(X, training=False).loader.num_workers,
|
|
250
|
+
loader_kwargs=loader_kwargs,
|
|
240
251
|
)
|
|
241
252
|
|
|
242
253
|
@property
|
{braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datasets/base.py
RENAMED
|
@@ -26,6 +26,7 @@ from itertools import chain
|
|
|
26
26
|
from operator import attrgetter
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
from typing import Any, Generic, Iterable, no_type_check
|
|
29
|
+
from uuid import uuid4
|
|
29
30
|
|
|
30
31
|
import mne
|
|
31
32
|
import mne.io
|
|
@@ -343,6 +344,9 @@ def _read_fif_window(raw, start, stop, willneed=False):
|
|
|
343
344
|
return np.multiply(picked, raw._cals[:, np.newaxis], order="C")
|
|
344
345
|
|
|
345
346
|
|
|
347
|
+
_COALESCED_MAX_BYTES = 2 * 1024 * 1024
|
|
348
|
+
|
|
349
|
+
|
|
346
350
|
def _window_info(crop_inds, sfreq):
|
|
347
351
|
"""Extract window size from crop indices.
|
|
348
352
|
|
|
@@ -479,14 +483,14 @@ def _zarr_to_memmap(zarr_path, group_name):
|
|
|
479
483
|
``SIGBUS`` when workers page-faulted on the unlinked inode.
|
|
480
484
|
|
|
481
485
|
Under ``N`` concurrent writers, every racer materialises the data
|
|
482
|
-
into its own per-
|
|
486
|
+
into its own per-call temp file and then tries to publish it; at
|
|
483
487
|
most one ``os.link`` wins, and the losers silently ``unlink``
|
|
484
488
|
their temp file in a ``finally`` block. The wasted I/O scales
|
|
485
489
|
with the number of racers; for the workloads braindecode targets
|
|
486
490
|
(a handful of concurrent workers per group) it is negligible.
|
|
487
491
|
|
|
488
492
|
Crash recovery: if a writer dies mid-run the only debris is a
|
|
489
|
-
``<group>.<
|
|
493
|
+
``<group>.<uuid>.tmp.npy`` file. Such files are unique per call
|
|
490
494
|
so they can never collide with a live writer, they are never
|
|
491
495
|
read by the fast path, and they do not prevent subsequent
|
|
492
496
|
callers from producing a correct result. Caches written by
|
|
@@ -504,7 +508,10 @@ def _zarr_to_memmap(zarr_path, group_name):
|
|
|
504
508
|
|
|
505
509
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
506
510
|
arr = zarr.open(zarr_path, mode="r")[group_name]["data"]
|
|
507
|
-
|
|
511
|
+
# Unique per call, not per pid: threads of one process must not share it.
|
|
512
|
+
# Exclusive creation, not tempfile.mkstemp, which would make it 0600.
|
|
513
|
+
tmp_path = cache_dir / f"{group_name}.{uuid4().hex}.tmp.npy"
|
|
514
|
+
tmp_path.open("xb").close()
|
|
508
515
|
try:
|
|
509
516
|
# Write chunk-by-chunk to avoid materialising the whole zarr
|
|
510
517
|
# array in RAM.
|
|
@@ -531,9 +538,9 @@ def _zarr_to_memmap(zarr_path, group_name):
|
|
|
531
538
|
# canonical one; ours is discarded in the finally block.
|
|
532
539
|
pass
|
|
533
540
|
finally:
|
|
534
|
-
# Always remove our private per-
|
|
535
|
-
#
|
|
536
|
-
#
|
|
541
|
+
# Always remove our private per-call temp file. Its name is
|
|
542
|
+
# unique per call so this cannot clobber a concurrent writer's
|
|
543
|
+
# in-progress tmp file.
|
|
537
544
|
try:
|
|
538
545
|
os.unlink(tmp_path)
|
|
539
546
|
except FileNotFoundError:
|
|
@@ -953,6 +960,10 @@ class EEGWindowsDataset(_ZarrMixin, RecordDataset):
|
|
|
953
960
|
X = self.raw._getitem(
|
|
954
961
|
(slice(None), slice(i_start, i_stop)), return_times=False
|
|
955
962
|
)
|
|
963
|
+
return self._finish_window(index, crop_inds, X)
|
|
964
|
+
|
|
965
|
+
def _finish_window(self, index, crop_inds, X):
|
|
966
|
+
"""Item ``index`` from its raw samples ``X``, copied before the transform."""
|
|
956
967
|
X = np.array(X, dtype="float32")
|
|
957
968
|
if self.transform is not None:
|
|
958
969
|
X = self.transform(X)
|
|
@@ -1344,11 +1355,24 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1344
1355
|
Optional function to call on targets before returning them.
|
|
1345
1356
|
lazy : bool, default False
|
|
1346
1357
|
If True, defer computing cumulative sizes until length or item access.
|
|
1358
|
+
coalesce_reads : bool, default False
|
|
1359
|
+
Opt in to reading a :class:`~torch.utils.data.DataLoader` batch of
|
|
1360
|
+
consecutive, overlapping windows of one lazy (``preload=False``) FIF
|
|
1361
|
+
recording as one union of samples instead of one read per window.
|
|
1362
|
+
Only a batch of consecutive ascending indices into one
|
|
1363
|
+
:class:`~braindecode.datasets.EEGWindowsDataset` (not a subclass)
|
|
1364
|
+
whose union is at most a third of the summed window samples and at
|
|
1365
|
+
most 2 MiB as float64 is read this way; every other batch takes the
|
|
1366
|
+
default path. Outputs, order and random number use equal the
|
|
1367
|
+
default's. It was faster only for heavily overlapping windows and
|
|
1368
|
+
slower for some other layouts (numbers in :ref:`whats_new`), so it is off
|
|
1369
|
+
by default. Containers returned by ``split`` default to ``False``.
|
|
1347
1370
|
"""
|
|
1348
1371
|
|
|
1349
1372
|
plot = _viewer_plot # eegdash-viewer embed; a direct member so the API docs list it
|
|
1350
1373
|
|
|
1351
1374
|
datasets: list[T]
|
|
1375
|
+
coalesce_reads: bool = False # default for pickles made before this option
|
|
1352
1376
|
|
|
1353
1377
|
def __init__(
|
|
1354
1378
|
self,
|
|
@@ -1358,6 +1382,7 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1358
1382
|
target_transform: Callable | None = None,
|
|
1359
1383
|
*,
|
|
1360
1384
|
lazy: bool = False,
|
|
1385
|
+
coalesce_reads: bool = False,
|
|
1361
1386
|
):
|
|
1362
1387
|
# Adapted from torch.utils.data.ConcatDataset:
|
|
1363
1388
|
# added lazy init to defer cumulative size computation until access,
|
|
@@ -1387,6 +1412,9 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1387
1412
|
|
|
1388
1413
|
self.target_transform = target_transform
|
|
1389
1414
|
self._lazy = lazy
|
|
1415
|
+
if type(coalesce_reads) is not bool:
|
|
1416
|
+
raise TypeError("coalesce_reads must be a bool.")
|
|
1417
|
+
self.coalesce_reads = coalesce_reads
|
|
1390
1418
|
|
|
1391
1419
|
@property
|
|
1392
1420
|
def cumulative_sizes(self) -> list[int]:
|
|
@@ -1454,6 +1482,10 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1454
1482
|
another: on a network file system a batch read from cold files then
|
|
1455
1483
|
costs about one round trip, not one per window.
|
|
1456
1484
|
"""
|
|
1485
|
+
if self.coalesce_reads:
|
|
1486
|
+
coalesced = self._get_coalesced_items(indices)
|
|
1487
|
+
if coalesced is not None:
|
|
1488
|
+
return coalesced
|
|
1457
1489
|
# ponytail: advises the whole batch at once; chunk it if a batch's
|
|
1458
1490
|
# windows ever outgrow the page cache
|
|
1459
1491
|
if hasattr(os, "posix_fadvise"):
|
|
@@ -1466,6 +1498,52 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1466
1498
|
_read_fif_window(ds.raw, start, stop, willneed=True)
|
|
1467
1499
|
return [self[idx] for idx in indices]
|
|
1468
1500
|
|
|
1501
|
+
def _get_coalesced_items(self, indices):
|
|
1502
|
+
"""The items cut from one read of their union, or None if not eligible."""
|
|
1503
|
+
n = len(indices)
|
|
1504
|
+
if (
|
|
1505
|
+
type(self) is not BaseConcatDataset # subclasses may override items
|
|
1506
|
+
or n < 2
|
|
1507
|
+
or type(indices[0]) is not int
|
|
1508
|
+
or indices[0] < 0
|
|
1509
|
+
or indices != list(range(indices[0], indices[0] + n))
|
|
1510
|
+
):
|
|
1511
|
+
return None
|
|
1512
|
+
sizes = self._ensure_cumulative_sizes()
|
|
1513
|
+
k = bisect.bisect_right(sizes, indices[0])
|
|
1514
|
+
if k == len(sizes) or indices[-1] >= sizes[k]:
|
|
1515
|
+
return None # past the end, or across recordings
|
|
1516
|
+
ds = self.datasets[k]
|
|
1517
|
+
if (
|
|
1518
|
+
type(ds) is not EEGWindowsDataset
|
|
1519
|
+
or ds._zarr_data is not None
|
|
1520
|
+
or not isinstance(ds.crop_inds, np.ndarray) # lazy metadata
|
|
1521
|
+
or ds.raw.preload
|
|
1522
|
+
):
|
|
1523
|
+
return None
|
|
1524
|
+
first = indices[0] - (sizes[k - 1] if k else 0)
|
|
1525
|
+
crops = ds.crop_inds[first : first + n]
|
|
1526
|
+
start, stop = int(crops[:, 1].min()), int(crops[:, 2].max())
|
|
1527
|
+
if (
|
|
1528
|
+
start < 0
|
|
1529
|
+
or stop > ds.raw.n_times
|
|
1530
|
+
or (crops[:, 2] <= crops[:, 1]).any()
|
|
1531
|
+
or 3 * (stop - start) > (crops[:, 2] - crops[:, 1]).sum()
|
|
1532
|
+
or (stop - start) * ds.raw.info["nchan"] * 8 > _COALESCED_MAX_BYTES
|
|
1533
|
+
):
|
|
1534
|
+
return None
|
|
1535
|
+
union = _read_fif_window(ds.raw, start, stop)
|
|
1536
|
+
if union is None:
|
|
1537
|
+
return None
|
|
1538
|
+
items = []
|
|
1539
|
+
for i, crop in enumerate(crops.tolist(), first):
|
|
1540
|
+
X = union[:, crop[1] - start : crop[2] - start]
|
|
1541
|
+
item = ds._finish_window(i, crop, X)
|
|
1542
|
+
if self.target_transform is not None:
|
|
1543
|
+
item = item[:1] + (self.target_transform(item[1]),) + item[2:]
|
|
1544
|
+
items.append(item)
|
|
1545
|
+
return items
|
|
1546
|
+
|
|
1469
1547
|
def __getitem__(self, idx: int | list):
|
|
1470
1548
|
"""
|
|
1471
1549
|
---
|
|
@@ -1583,15 +1661,17 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1583
1661
|
df = ds._windows.metadata
|
|
1584
1662
|
else:
|
|
1585
1663
|
df = ds.metadata
|
|
1664
|
+
# a dataset built without description has description None
|
|
1665
|
+
description = ds.description if ds.description is not None else {}
|
|
1586
1666
|
if (
|
|
1587
1667
|
"i_trial_in_dataset" in df.columns
|
|
1588
|
-
and "i_trial_in_dataset" in
|
|
1668
|
+
and "i_trial_in_dataset" in description
|
|
1589
1669
|
):
|
|
1590
1670
|
raise ValueError(
|
|
1591
1671
|
"Dataset descriptions cannot contain the reserved window "
|
|
1592
1672
|
"metadata key 'i_trial_in_dataset'."
|
|
1593
1673
|
)
|
|
1594
|
-
if "i_dataset" in df.columns or "i_dataset" in
|
|
1674
|
+
if "i_dataset" in df.columns or "i_dataset" in description:
|
|
1595
1675
|
raise ValueError(
|
|
1596
1676
|
"Window metadata and dataset descriptions cannot contain the "
|
|
1597
1677
|
"key 'i_dataset', which get_metadata() reserves for the "
|
|
@@ -1607,7 +1687,8 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1607
1687
|
if isinstance(df, pd.DataFrame):
|
|
1608
1688
|
# copy so the description columns stay out of the dataset
|
|
1609
1689
|
df = df.copy()
|
|
1610
|
-
|
|
1690
|
+
description = ds.description if ds.description is not None else {}
|
|
1691
|
+
for k, v in description.items():
|
|
1611
1692
|
df[k] = v
|
|
1612
1693
|
df["i_dataset"] = i_ds
|
|
1613
1694
|
all_dfs.append(df)
|
|
@@ -224,15 +224,39 @@ class BIDSEpochsDataset(BIDSDataset):
|
|
|
224
224
|
)
|
|
225
225
|
|
|
226
226
|
def _set_metadata(self, epochs: mne.BaseEpochs) -> None:
|
|
227
|
-
# events = mne.events_from_annotations(epochs
|
|
228
227
|
n_times = epochs.times.shape[0]
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if annotations is not None:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
228
|
+
id_events = {v: k for k, v in epochs.event_id.items()}
|
|
229
|
+
target = [id_events[event_id] for event_id in epochs.events[:, -1]]
|
|
230
|
+
if epochs.annotations is not None and len(epochs.annotations) > 0:
|
|
231
|
+
# Match triggers on the original recording's sample clock, even if
|
|
232
|
+
# epochs have been cropped past time zero, decimated or resampled.
|
|
233
|
+
onset_samples = np.round(
|
|
234
|
+
epochs.annotations.onset * epochs._raw_sfreq
|
|
235
|
+
).astype(int)
|
|
236
|
+
at_zero = []
|
|
237
|
+
for sample in epochs.events[:, 0]:
|
|
238
|
+
matches = np.flatnonzero(onset_samples == sample)
|
|
239
|
+
if len(matches) == 0:
|
|
240
|
+
# FIF stores onsets as float32. Only the quantization bins
|
|
241
|
+
# containing this sample's half-sample boundaries can move
|
|
242
|
+
# a valid onset to an adjacent rounded sample on readback.
|
|
243
|
+
boundaries = np.asarray(
|
|
244
|
+
[
|
|
245
|
+
(sample - 0.5) / epochs._raw_sfreq,
|
|
246
|
+
(sample + 0.5) / epochs._raw_sfreq,
|
|
247
|
+
],
|
|
248
|
+
dtype=np.float32,
|
|
249
|
+
).astype(float)
|
|
250
|
+
matches = np.flatnonzero(
|
|
251
|
+
np.isin(epochs.annotations.onset, boundaries)
|
|
252
|
+
& (np.abs(onset_samples - sample) == 1)
|
|
253
|
+
)
|
|
254
|
+
at_zero.append(matches)
|
|
255
|
+
if all(len(matches) == 1 for matches in at_zero):
|
|
256
|
+
indices = [matches[0] for matches in at_zero]
|
|
257
|
+
# A boundary annotation cannot label two neighboring events.
|
|
258
|
+
if len(set(indices)) == len(set(epochs.events[:, 0])):
|
|
259
|
+
target = epochs.annotations.description[indices].tolist()
|
|
236
260
|
metadata_dict = {
|
|
237
261
|
"i_window_in_trial": np.zeros(len(epochs)),
|
|
238
262
|
"i_start_in_trial": np.zeros(len(epochs)),
|
|
@@ -13,6 +13,7 @@ from glob import glob
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
import mne
|
|
16
|
+
import numpy as np
|
|
16
17
|
import pandas as pd
|
|
17
18
|
from joblib import Parallel, delayed
|
|
18
19
|
|
|
@@ -196,7 +197,7 @@ def load_concat_dataset(path, preload, ids_to_load=None, target_name=None, n_job
|
|
|
196
197
|
Ids of specific files to load.
|
|
197
198
|
target_name : str | list | None
|
|
198
199
|
Load specific description column as target. If not given, take saved
|
|
199
|
-
target name.
|
|
200
|
+
target name. Only for datasets of raws.
|
|
200
201
|
n_jobs : int
|
|
201
202
|
Number of jobs to be used to read files in parallel.
|
|
202
203
|
|
|
@@ -245,10 +246,18 @@ def load_concat_dataset(path, preload, ids_to_load=None, target_name=None, n_job
|
|
|
245
246
|
UserWarning,
|
|
246
247
|
)
|
|
247
248
|
n_jobs = 1
|
|
249
|
+
if target_name is not None and (has_stored_windows or not is_raw):
|
|
250
|
+
raise ValueError(
|
|
251
|
+
"target_name can only be given for datasets of raws; for windowed "
|
|
252
|
+
"datasets, use BaseConcatDataset.set_target after loading."
|
|
253
|
+
)
|
|
248
254
|
datasets = Parallel(n_jobs)(
|
|
249
255
|
delayed(_load_parallel)(path, i, preload, is_raw, has_stored_windows)
|
|
250
256
|
for i in ids_to_load
|
|
251
257
|
)
|
|
258
|
+
if target_name is not None:
|
|
259
|
+
for ds in datasets:
|
|
260
|
+
ds.target_name = ds._target_name(target_name)
|
|
252
261
|
return BaseConcatDataset(datasets)
|
|
253
262
|
|
|
254
263
|
|
|
@@ -265,7 +274,11 @@ def _load_parallel(path, i, preload, is_raw, has_stored_windows):
|
|
|
265
274
|
signals = _load_signals(fif_file_path, preload, is_raw)
|
|
266
275
|
|
|
267
276
|
description_file_path = sub_dir / "description.json"
|
|
268
|
-
|
|
277
|
+
# dtype=False: keep strings such as subject "01" as strings
|
|
278
|
+
description = pd.read_json(
|
|
279
|
+
description_file_path, typ="series", convert_dates=False, dtype=False
|
|
280
|
+
)
|
|
281
|
+
description[description.isna()] = np.nan # JSON null was read as NaN
|
|
269
282
|
|
|
270
283
|
# if 'timestamp' in description.index:
|
|
271
284
|
# timestamp_numeric = pd.to_numeric(description['timestamp'])
|
{braindecode-1.8.1.dev169484576 → braindecode-1.8.1.dev169575464}/braindecode/datautil/util.py
RENAMED
|
@@ -142,10 +142,11 @@ def infer_signal_properties(
|
|
|
142
142
|
if isinstance(X, BaseConcatDataset) and all(
|
|
143
143
|
ds.targets_from == "metadata" for ds in X.datasets
|
|
144
144
|
):
|
|
145
|
-
|
|
145
|
+
# an array: several targets per window give one column each
|
|
146
|
+
y_target = np.asarray(X.get_metadata().target.to_list())
|
|
146
147
|
signal_kwargs["n_outputs"] = _get_n_outputs(y_target, classes, mode)
|
|
147
148
|
elif isinstance(X, WindowsDataset) and X.targets_from == "metadata":
|
|
148
|
-
y_target = X.windows.metadata.target
|
|
149
|
+
y_target = np.asarray(X.windows.metadata.target.to_list())
|
|
149
150
|
signal_kwargs["n_outputs"] = _get_n_outputs(y_target, classes, mode)
|
|
150
151
|
else:
|
|
151
152
|
log.warning(
|