Braindecode 1.8.1.dev174738705__tar.gz → 1.8.1.dev174812945__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.dev174738705/braindecode.egg-info → braindecode-1.8.1.dev174812945}/PKG-INFO +1 -1
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/base.py +46 -3
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/functional/_real_dft.py +55 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/functional/functions.py +13 -5
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/barista.py +2 -2
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/codebrain.py +27 -14
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/contrawr.py +23 -1
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/csbrain.py +69 -36
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/diver1.py +51 -59
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eeginception_mi.py +7 -3
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegnex.py +1 -1
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegpt.py +15 -9
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/labram.py +19 -9
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/luna.py +3 -1
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/medformer.py +13 -13
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/meta_neuromotor.py +35 -8
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/neurorvq_tokenizer.py +10 -1
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/patchedtransformer.py +3 -7
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/reve.py +16 -4
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/zuna.py +11 -9
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/attention.py +53 -6
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/dance_modules.py +16 -3
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/layers.py +41 -2
- braindecode-1.8.1.dev174812945/braindecode/version.py +1 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945/braindecode.egg-info}/PKG-INFO +1 -1
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/whats_new.rst +32 -0
- braindecode-1.8.1.dev174738705/braindecode/version.py +0 -1
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/LICENSE.txt +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/MANIFEST.in +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/NOTICE.txt +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/README.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/augmentation/base.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/augmentation/functional.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/augmentation/transforms.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/classifier.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/_notebook_viewer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bids/datasets.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bids/format.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bids/hub.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/collate.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datautil/serialization.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datautil/util.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/eegneuralnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/functional/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/atcnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/axon.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/base.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/bendr.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/biot.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/brainbert.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/brainmodule.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/brainomni.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/brant.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/config.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/ctnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/dance.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/deep4.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eeg_clip.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegconformer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegdino.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eeginception_erp.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegitnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegminer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegsym.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/emg2qwerty.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/fbmsnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/guetschel2026.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/mapa.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/mirepnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/mscformer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/msvtnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/mvpformer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/neuropose.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/neurorvq.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/popt.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/seizure_transformer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/sensingdynamics.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/shallow_fbcsp.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/signal_jepa.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/sinc_shallow.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/sleepfm.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/steegformer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/summary.csv +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/tcformer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/tcn.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/tfm_tokenizer.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/tmsanet.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/usleep.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/util.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/vemg2pose.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/activation.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/blocks.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/channels.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/convolution.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/filter.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/linear.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/linear_attention.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/parametrization.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/popt_modules.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/quantization.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/stats.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/util.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/preprocessing/mne_preprocess.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/preprocessing/preprocess.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/preprocessing/windowers.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/regressor.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/samplers/base.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/training/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/training/losses.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/training/scoring.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/util.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/visualization/activations.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/visualization/attribution.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/visualization/frequency.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/visualization/metrics.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/visualization/sanity.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/visualization/topology.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode.egg-info/SOURCES.txt +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode.egg-info/requires.txt +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/Makefile +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/agent_docs.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/api.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/cite.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/conf.py +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/help.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/index.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/install/install.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/install/install_pip.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/install/install_source.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/models.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/models_table.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/docs/user_guide/channel_strategies.rst +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/pyproject.toml +0 -0
- {braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/setup.cfg +0 -0
{braindecode-1.8.1.dev174738705/braindecode.egg-info → braindecode-1.8.1.dev174812945}/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.dev174812945
|
|
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>
|
{braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/datasets/base.py
RENAMED
|
@@ -279,7 +279,7 @@ def _compact_fif_tags(inst):
|
|
|
279
279
|
return inst
|
|
280
280
|
|
|
281
281
|
|
|
282
|
-
def _read_fif_window(raw, start, stop):
|
|
282
|
+
def _read_fif_window(raw, start, stop, willneed=False):
|
|
283
283
|
"""Samples ``start:stop`` of a lazy FIF raw, copied from the file in one go.
|
|
284
284
|
|
|
285
285
|
mne reads, casts and calibrates the FIF data buffers of a range one at a
|
|
@@ -290,6 +290,10 @@ def _read_fif_window(raw, start, stop):
|
|
|
290
290
|
times ``raw._cals``), so the values equal ``raw._getitem``'s. Returns None
|
|
291
291
|
(the caller uses mne) unless ``raw`` is a lazy, single-file, uncompressed
|
|
292
292
|
FIF raw of float32 buffers without projector, compensation or gaps.
|
|
293
|
+
|
|
294
|
+
With ``willneed=True`` nothing is read and None is returned: the OS is
|
|
295
|
+
only asked to start reading the window's bytes into the page cache in the
|
|
296
|
+
background (``posix_fadvise(POSIX_FADV_WILLNEED)``).
|
|
293
297
|
"""
|
|
294
298
|
if (
|
|
295
299
|
not isinstance(raw, mne.io.Raw)
|
|
@@ -316,6 +320,18 @@ def _read_fif_window(raw, start, stop):
|
|
|
316
320
|
if not len(rows) or (rows[:, 1] != FIFF.FIFFT_FLOAT).any():
|
|
317
321
|
return None
|
|
318
322
|
size, pos = rows[:, 2].tolist(), rows[:, 4].tolist()
|
|
323
|
+
if willneed: # only a hint: any error is left to the read itself
|
|
324
|
+
try:
|
|
325
|
+
with open(fname, "rb") as f:
|
|
326
|
+
os.posix_fadvise(
|
|
327
|
+
f.fileno(),
|
|
328
|
+
pos[0],
|
|
329
|
+
pos[-1] + 16 + size[-1] - pos[0],
|
|
330
|
+
os.POSIX_FADV_WILLNEED,
|
|
331
|
+
)
|
|
332
|
+
except OSError:
|
|
333
|
+
pass
|
|
334
|
+
return None
|
|
319
335
|
# mmap: no copy through a 4 MB read buffer, and a cold file is read around
|
|
320
336
|
# the window by the kernel (as mne's 4 MB reads did), not window by window
|
|
321
337
|
with open(fname, "rb") as f, mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
|
|
@@ -1362,7 +1378,8 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1362
1378
|
def __len__(self):
|
|
1363
1379
|
return self._ensure_cumulative_sizes()[-1]
|
|
1364
1380
|
|
|
1365
|
-
def
|
|
1381
|
+
def _locate(self, idx: int):
|
|
1382
|
+
"""The dataset that holds item ``idx`` and the item's index in it."""
|
|
1366
1383
|
if idx < 0:
|
|
1367
1384
|
if -idx > len(self):
|
|
1368
1385
|
raise ValueError(
|
|
@@ -1376,7 +1393,33 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1376
1393
|
sample_idx = idx
|
|
1377
1394
|
else:
|
|
1378
1395
|
sample_idx = idx - cumulative_sizes[dataset_idx - 1]
|
|
1379
|
-
return self.datasets[dataset_idx]
|
|
1396
|
+
return self.datasets[dataset_idx], sample_idx
|
|
1397
|
+
|
|
1398
|
+
def _get_single_item(self, idx: int):
|
|
1399
|
+
ds, i = self._locate(idx)
|
|
1400
|
+
return ds[i]
|
|
1401
|
+
|
|
1402
|
+
def __getitems__(self, indices):
|
|
1403
|
+
"""Items ``indices``, as ``[self[i] for i in indices]``.
|
|
1404
|
+
|
|
1405
|
+
A :class:`~torch.utils.data.DataLoader` fetches each batch through
|
|
1406
|
+
this. The lazy FIF windows of the batch are first requested from the
|
|
1407
|
+
OS all at once (``posix_fadvise(POSIX_FADV_WILLNEED)``, where it
|
|
1408
|
+
exists), so their reads from disk overlap instead of running one after
|
|
1409
|
+
another: on a network file system a batch read from cold files then
|
|
1410
|
+
costs about one round trip, not one per window.
|
|
1411
|
+
"""
|
|
1412
|
+
# ponytail: advises the whole batch at once; chunk it if a batch's
|
|
1413
|
+
# windows ever outgrow the page cache
|
|
1414
|
+
if hasattr(os, "posix_fadvise"):
|
|
1415
|
+
for idx in indices:
|
|
1416
|
+
if not isinstance(idx, (int, np.integer)):
|
|
1417
|
+
continue # e.g. a SequenceSampler tuple: read, not advised
|
|
1418
|
+
ds, i = self._locate(idx)
|
|
1419
|
+
if isinstance(ds, EEGWindowsDataset) and ds._zarr_data is None:
|
|
1420
|
+
_, start, stop = ds.crop_inds[i].tolist()
|
|
1421
|
+
_read_fif_window(ds.raw, start, stop, willneed=True)
|
|
1422
|
+
return [self[idx] for idx in indices]
|
|
1380
1423
|
|
|
1381
1424
|
def __getitem__(self, idx: int | list):
|
|
1382
1425
|
"""
|
|
@@ -12,8 +12,10 @@ never downcast, so callers wrap the numerically sensitive parts with
|
|
|
12
12
|
#
|
|
13
13
|
# License: BSD (3-clause)
|
|
14
14
|
import functools
|
|
15
|
+
from typing import Optional
|
|
15
16
|
|
|
16
17
|
import torch
|
|
18
|
+
from torch import nn
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
def needs_real_dft(x: torch.Tensor) -> bool:
|
|
@@ -136,3 +138,56 @@ def hilbert_freq_real(x: torch.Tensor, forward_fourier: bool) -> torch.Tensor:
|
|
|
136
138
|
return torch.stack((out_real, out_imag), dim=-1)
|
|
137
139
|
|
|
138
140
|
return compute(x)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@torch.jit.unused
|
|
144
|
+
def fft_conv1d_real(
|
|
145
|
+
x: torch.Tensor, weight: torch.Tensor, bias: Optional[torch.Tensor]
|
|
146
|
+
) -> torch.Tensor:
|
|
147
|
+
"""Real-valued equivalent of :func:`braindecode.functional.fft_conv1d`.
|
|
148
|
+
|
|
149
|
+
Overlap-save: the input is cut into blocks of ``n`` samples (the power of
|
|
150
|
+
two ``>= 2 * kernel_size``) that advance by ``n - kernel_size + 1`` outputs,
|
|
151
|
+
each block's DFT is a matmul against the cosine/sine basis, and per
|
|
152
|
+
frequency the complex product with the kernels, summed over the input
|
|
153
|
+
channels, is one real matmul.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
@fp32_island
|
|
157
|
+
def compute(x, weight):
|
|
158
|
+
n_times, kernel_size = x.shape[-1], weight.shape[-1]
|
|
159
|
+
n = 1 << (2 * kernel_size - 1).bit_length()
|
|
160
|
+
hop = n - kernel_size + 1
|
|
161
|
+
n_blocks = -(-n_times // hop)
|
|
162
|
+
n_freqs = n // 2 + 1
|
|
163
|
+
# "same" alignment: torch pads the extra zero of an even kernel on the right
|
|
164
|
+
left = (kernel_size - 1) // 2
|
|
165
|
+
x = nn.functional.pad(x, (left, (n_blocks + 1) * hop - n_times - left))
|
|
166
|
+
hops = x.unflatten(-1, (n_blocks + 1, hop))
|
|
167
|
+
# block i = hop i + the first kernel_size - 1 samples of hop i + 1
|
|
168
|
+
blocks = torch.cat([hops[:, :, :-1], hops[:, :, 1:, : kernel_size - 1]], -1)
|
|
169
|
+
cosine, sine = real_dft_basis(n, device=x.device, dtype=x.dtype)
|
|
170
|
+
basis = torch.cat([cosine, -sine]).transpose(0, 1) # -> [Re | Im] of rfft
|
|
171
|
+
x_f = blocks @ basis # (batch, in, blocks, 2 * n_freqs)
|
|
172
|
+
w_f = nn.functional.pad(weight.to(x), (0, n - kernel_size)) @ basis
|
|
173
|
+
w_re, w_im = w_f[..., :n_freqs], w_f[..., n_freqs:]
|
|
174
|
+
# conv1d is a cross-correlation: x_f * conj(w_f), summed over the input
|
|
175
|
+
# channels. Per frequency [Re x, Im x] @ [[Re w, -Im w], [Im w, Re w]].
|
|
176
|
+
w_f = torch.cat([torch.cat([w_re, -w_im]), torch.cat([w_im, w_re])], 1)
|
|
177
|
+
x_f = x_f.unflatten(-1, (2, n_freqs)).permute(4, 0, 2, 3, 1).flatten(1, 2)
|
|
178
|
+
y_f = torch.bmm(x_f.flatten(2), w_f.permute(2, 1, 0)) # (freq, ., 2 * out)
|
|
179
|
+
y_f = y_f.unflatten(1, (x.shape[0], n_blocks)).unflatten(-1, (2, -1))
|
|
180
|
+
y_f = y_f.permute(1, 4, 2, 3, 0).flatten(
|
|
181
|
+
-2
|
|
182
|
+
) # (batch, out, blocks, 2 * n_freqs)
|
|
183
|
+
# inverse DFT of the first `hop` samples, the ones without wrap-around
|
|
184
|
+
scale = torch.full((n_freqs, 1), 2.0 / n, dtype=x.dtype, device=x.device)
|
|
185
|
+
scale[0] = 1.0 / n
|
|
186
|
+
scale[-1] = 1.0 / n # Nyquist (n is even)
|
|
187
|
+
inverse = torch.cat([scale * cosine[:, :hop], -scale * sine[:, :hop]])
|
|
188
|
+
return (y_f @ inverse).flatten(-2)[..., :n_times]
|
|
189
|
+
|
|
190
|
+
y = compute(x, weight)
|
|
191
|
+
if bias is not None:
|
|
192
|
+
y = y + bias.to(y).unsqueeze(-1)
|
|
193
|
+
return y.to(x)
|
|
@@ -62,9 +62,10 @@ def fft_conv1d(
|
|
|
62
62
|
|
|
63
63
|
Equal to the direct convolution up to float rounding: the FFT is long
|
|
64
64
|
enough for a linear (not circular) convolution. Its cost barely grows with
|
|
65
|
-
the kernel size, so it is faster than the direct one for long kernels
|
|
66
|
-
|
|
67
|
-
float32 (:func:`spectral_input`) and cast back.
|
|
65
|
+
the kernel size, so it is faster than the direct one for long kernels
|
|
66
|
+
(see :func:`prefer_fft_conv`). float16/bfloat16 inputs are computed in
|
|
67
|
+
float32 (:func:`spectral_input`) and cast back. On Intel Gaudi (HPU, no
|
|
68
|
+
complex dtype) the DFTs are real matmuls over overlapping blocks.
|
|
68
69
|
|
|
69
70
|
Parameters
|
|
70
71
|
----------
|
|
@@ -82,6 +83,8 @@ def fft_conv1d(
|
|
|
82
83
|
``x``. For an even ``kernel_size`` it is aligned like torch's
|
|
83
84
|
``padding="same"`` (the extra zero padded on the right).
|
|
84
85
|
"""
|
|
86
|
+
if _real_dft.needs_real_dft(x):
|
|
87
|
+
return _real_dft.fft_conv1d_real(x, weight, bias)
|
|
85
88
|
n_times, kernel_size = x.shape[-1], weight.shape[-1]
|
|
86
89
|
# >= n_times + kernel_size - 1 (no circular wrap-around), even and
|
|
87
90
|
# 5-smooth: the fast cases of a real FFT
|
|
@@ -115,8 +118,11 @@ def prefer_fft_conv(x: torch.Tensor, kernel_size: int) -> bool:
|
|
|
115
118
|
and from ``batch_size * kernel_size >= 600`` if also
|
|
116
119
|
``in_channels * kernel_size >= 120``: with one or two input channels a
|
|
117
120
|
float32 direct convolution stays cheaper up to k = 85 / 31 (13 long
|
|
118
|
-
temporal convolutions of the model zoo, 22 channels x batch 32).
|
|
119
|
-
|
|
121
|
+
temporal convolutions of the model zoo, 22 channels x batch 32). On Intel
|
|
122
|
+
Gaudi (HPU) a direct convolution costs about ``batch_size * n_times *
|
|
123
|
+
kernel_size`` and the FFT about a fixed few milliseconds in eager mode: the
|
|
124
|
+
FFT wins from 8 input channels and ``batch_size * n_times * kernel_size >=
|
|
125
|
+
8e6``. GPUs keep the direct convolution.
|
|
120
126
|
|
|
121
127
|
Parameters
|
|
122
128
|
----------
|
|
@@ -125,6 +131,8 @@ def prefer_fft_conv(x: torch.Tensor, kernel_size: int) -> bool:
|
|
|
125
131
|
kernel_size : int
|
|
126
132
|
Kernel length in samples.
|
|
127
133
|
"""
|
|
134
|
+
if x.device.type == "hpu":
|
|
135
|
+
return x.shape[1] >= 8 and x.shape[0] * x.shape[-1] * kernel_size >= 8e6
|
|
128
136
|
return x.device.type == "cpu" and (
|
|
129
137
|
x.element_size() == 2
|
|
130
138
|
or (x.shape[0] * kernel_size >= 600 and x.shape[1] * kernel_size >= 120)
|
{braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/barista.py
RENAMED
|
@@ -7,13 +7,13 @@ from __future__ import annotations
|
|
|
7
7
|
from typing import Optional
|
|
8
8
|
|
|
9
9
|
import torch
|
|
10
|
-
import torch.nn.functional as F
|
|
11
10
|
from einops.layers.torch import Rearrange
|
|
12
11
|
from torch import nn
|
|
13
12
|
|
|
14
13
|
from braindecode.models.base import EEGModuleMixin
|
|
15
14
|
from braindecode.models.util import extract_channel_locations_from_chs_info
|
|
16
15
|
from braindecode.modules import GatedLinearUnit, PatchTokenizer
|
|
16
|
+
from braindecode.modules.attention import _scaled_dot_product_attention
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class BaRISTA(EEGModuleMixin, nn.Module, license="other"):
|
|
@@ -689,7 +689,7 @@ class _RotarySelfAttention(nn.Module):
|
|
|
689
689
|
|
|
690
690
|
# Dropout falls on the attention output rather than on the weights,
|
|
691
691
|
# through dropout_p, because the reference disables the latter outright.
|
|
692
|
-
attention =
|
|
692
|
+
attention = _scaled_dot_product_attention(query, key, value)
|
|
693
693
|
attention = self.dropout(attention)
|
|
694
694
|
attention = self.merge_heads(attention)
|
|
695
695
|
return self.o_proj(attention)
|
{braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/codebrain.py
RENAMED
|
@@ -15,6 +15,7 @@ from torch import nn
|
|
|
15
15
|
from torch.nn.utils.parametrizations import weight_norm
|
|
16
16
|
|
|
17
17
|
from braindecode.functional import spectral_input
|
|
18
|
+
from braindecode.functional.functions import _next_fast_len
|
|
18
19
|
from braindecode.models.base import EEGModuleMixin
|
|
19
20
|
|
|
20
21
|
|
|
@@ -521,16 +522,21 @@ class _GConv(nn.Module):
|
|
|
521
522
|
kernel = F.pad(k_fwd, (0, seq_len)) + F.pad(k_bwd.flip(-1), (seq_len, 0))
|
|
522
523
|
|
|
523
524
|
# FFT-based convolution: O(N log N)
|
|
525
|
+
# The bidirectional kernel is circular (lags 0..L-1, then -L..-1).
|
|
526
|
+
# Rolled to lags -L..L-1 it is a linear convolution read from lag 0,
|
|
527
|
+
# so any FFT length >= 2 * seq_len works: take a 5-smooth (fast) one.
|
|
528
|
+
start = seq_len if self.bidirectional else 0
|
|
529
|
+
n_fft = 2 * _next_fast_len(seq_len)
|
|
524
530
|
# kernel_freq: (channels, d_model, freq_bins)
|
|
525
531
|
# spectral_input(x) dtype: x's, at least float32 (the reference's .float()).
|
|
526
532
|
x_in = spectral_input(x)
|
|
527
|
-
kernel_freq = torch.fft.rfft(kernel.to(x_in), n=
|
|
533
|
+
kernel_freq = torch.fft.rfft(kernel.roll(start, -1).to(x_in), n=n_fft)
|
|
528
534
|
# x_freq: (batch, d_model, freq_bins)
|
|
529
|
-
x_freq = torch.fft.rfft(x_in, n=
|
|
535
|
+
x_freq = torch.fft.rfft(x_in, n=n_fft)
|
|
530
536
|
# out_freq: (batch, channels, d_model, freq_bins)
|
|
531
537
|
out_freq = torch.einsum("bhl,chl->bchl", x_freq, kernel_freq)
|
|
532
538
|
# out: (batch, channels, d_model, seq_len)
|
|
533
|
-
out = torch.fft.irfft(out_freq, n=
|
|
539
|
+
out = torch.fft.irfft(out_freq, n=n_fft)[..., start : start + seq_len].to(x)
|
|
534
540
|
|
|
535
541
|
# Skip connection via learnable D matrix
|
|
536
542
|
# (batch, channels, d_model, seq_len)
|
|
@@ -697,18 +703,15 @@ class _ResidualBlock(nn.Module):
|
|
|
697
703
|
# Sliding-window attention branch
|
|
698
704
|
# (batch, 2*res_channels, seq_len) -> (batch, seq_len, 2*res_channels)
|
|
699
705
|
h_attn = h_ssm.transpose(1, 2)
|
|
700
|
-
if
|
|
701
|
-
self.swa_window_size == 1
|
|
702
|
-
and not self.training
|
|
703
|
-
and not torch.is_grad_enabled()
|
|
704
|
-
and h_attn.device.type == "cpu"
|
|
705
|
-
):
|
|
706
|
+
if self.swa_window_size == 1 and h_attn.device.type == "cpu":
|
|
706
707
|
# A one-wide window keeps only the diagonal: the softmax weights are
|
|
707
|
-
# exactly 1 and 0, so the attention returns its value projection
|
|
708
|
-
#
|
|
709
|
-
#
|
|
710
|
-
#
|
|
711
|
-
#
|
|
708
|
+
# exactly 1 and 0, so the attention returns its value projection,
|
|
709
|
+
# times the weights' dropout in training (the same (batch * heads,
|
|
710
|
+
# L, L) mask nn.MultiheadAttention draws, of which only the
|
|
711
|
+
# diagonal reaches the output). Same projection calls as
|
|
712
|
+
# nn.MultiheadAttention (on its (seq, batch, embed) view), so
|
|
713
|
+
# outputs and gradients are unchanged bit for bit, and so is the
|
|
714
|
+
# RNG stream. (On Gaudi the full attention was faster.)
|
|
712
715
|
# ponytail: the unused query/key projections are computed too, to
|
|
713
716
|
# keep the rounding; in_proj_weight[2E:] alone is ~2x less GEMM.
|
|
714
717
|
embed_dim = self.attention.embed_dim
|
|
@@ -716,6 +719,16 @@ class _ResidualBlock(nn.Module):
|
|
|
716
719
|
value = F.linear(
|
|
717
720
|
seq_first, self.attention.in_proj_weight, self.attention.in_proj_bias
|
|
718
721
|
)[..., 2 * embed_dim :]
|
|
722
|
+
p = self.attention.dropout
|
|
723
|
+
if self.training and p > 0.0:
|
|
724
|
+
n_rows = seq_first.shape[1] * self.attention.num_heads
|
|
725
|
+
keep = (
|
|
726
|
+
value.new_empty(n_rows, seq_len, seq_len)
|
|
727
|
+
.bernoulli_(1 - p)
|
|
728
|
+
.diagonal(dim1=1, dim2=2)
|
|
729
|
+
.div(1 - p)
|
|
730
|
+
)
|
|
731
|
+
value = value.reshape(seq_len, n_rows, -1) * keep.t().unsqueeze(-1)
|
|
719
732
|
h_attn = (
|
|
720
733
|
self.attention.out_proj(value.reshape(-1, embed_dim))
|
|
721
734
|
.view(seq_first.shape)
|
{braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/contrawr.py
RENAMED
|
@@ -4,7 +4,7 @@ import torch
|
|
|
4
4
|
import torch.nn as nn
|
|
5
5
|
from mne.utils import warn
|
|
6
6
|
|
|
7
|
-
from braindecode.functional import spectral_input
|
|
7
|
+
from braindecode.functional import _real_dft, spectral_input
|
|
8
8
|
from braindecode.models.base import EEGModuleMixin
|
|
9
9
|
from braindecode.models.util import _disable_batch_norm_training_if_batch_size_one
|
|
10
10
|
|
|
@@ -297,6 +297,8 @@ class _STFTModule(nn.Module):
|
|
|
297
297
|
|
|
298
298
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
299
299
|
# x: (B, C, T)
|
|
300
|
+
if _real_dft.needs_real_dft(x) and self.center and self.one_sided:
|
|
301
|
+
return self._forward_real_dft(x).to(x)
|
|
300
302
|
B, C, T = x.shape
|
|
301
303
|
# flatten batch & channel into one dim
|
|
302
304
|
x_flat = spectral_input(x.reshape(B * C, T))
|
|
@@ -319,3 +321,23 @@ class _STFTModule(nn.Module):
|
|
|
319
321
|
spec = spec_flat.view(B, C, F, L)
|
|
320
322
|
|
|
321
323
|
return torch.abs(spec).to(x)
|
|
324
|
+
|
|
325
|
+
@torch.jit.unused
|
|
326
|
+
@_real_dft.fp32_island
|
|
327
|
+
def _forward_real_dft(self, x):
|
|
328
|
+
# The magnitude above without complex tensors, on the device (Intel
|
|
329
|
+
# Gaudi / HPU), instead of a host STFT and a host-to-device copy of
|
|
330
|
+
# the whole spectrogram: reflect-padded frames (gathered, not a
|
|
331
|
+
# strided unfold) times the one-sided DFT basis. No annotations: with
|
|
332
|
+
# postponed annotations torch.jit.unused cannot resolve them.
|
|
333
|
+
pad = self.n_fft // 2
|
|
334
|
+
x = nn.functional.pad(x, (pad, pad), mode="reflect")
|
|
335
|
+
n_frames = 1 + (x.shape[-1] - self.n_fft) // self.hop_length
|
|
336
|
+
starts = torch.arange(n_frames, device=x.device) * self.hop_length
|
|
337
|
+
index = (starts[:, None] + torch.arange(self.n_fft, device=x.device)).flatten()
|
|
338
|
+
frames = x.index_select(-1, index).unflatten(-1, (n_frames, self.n_fft))
|
|
339
|
+
real, imag = _real_dft.real_rfft(frames)
|
|
340
|
+
magnitude = torch.sqrt(real * real + imag * imag)
|
|
341
|
+
if self.normalized:
|
|
342
|
+
magnitude = magnitude / self.n_fft**0.5
|
|
343
|
+
return magnitude.transpose(-1, -2)
|
{braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/csbrain.py
RENAMED
|
@@ -541,6 +541,7 @@ class _RegionEmbedding(nn.Module):
|
|
|
541
541
|
"""
|
|
542
542
|
|
|
543
543
|
region_bounds: List[Tuple[int, int]]
|
|
544
|
+
tap_starts: List[int]
|
|
544
545
|
|
|
545
546
|
def __init__(
|
|
546
547
|
self,
|
|
@@ -574,42 +575,61 @@ class _RegionEmbedding(nn.Module):
|
|
|
574
575
|
for region_key in area_config
|
|
575
576
|
}
|
|
576
577
|
)
|
|
578
|
+
# All regions in one pass: tap j of the widest kernel reads, for every
|
|
579
|
+
# electrode, the electrode at offset j - half on its region's ring
|
|
580
|
+
# (circular padding), or nothing (zero padding of a one-electrode
|
|
581
|
+
# region); kernel k uses the middle k taps. As a 0/1 matrix, so the
|
|
582
|
+
# gather and its backward are matmuls.
|
|
583
|
+
half = (max(kernel_sizes) - 1) // 2
|
|
584
|
+
n_taps = 2 * half + 1
|
|
585
|
+
self.tap_starts = [half - (k - 1) // 2 for k in kernel_sizes]
|
|
586
|
+
n_chans = self.region_bounds[-1][1] if self.region_bounds else 0
|
|
587
|
+
chan_region = []
|
|
588
|
+
tap_select = torch.zeros(n_chans * n_taps, n_chans)
|
|
589
|
+
for i, (start, stop) in enumerate(self.region_bounds):
|
|
590
|
+
n = stop - start
|
|
591
|
+
for p in range(n):
|
|
592
|
+
chan_region.append(i)
|
|
593
|
+
for d in range(-half, half + 1) if n > 1 else [0]:
|
|
594
|
+
row = (start + p) * n_taps + d + half
|
|
595
|
+
tap_select[row, start + (p + d) % n] = 1.0
|
|
596
|
+
self.register_buffer(
|
|
597
|
+
"chan_region", torch.tensor(chan_region, dtype=torch.long), False
|
|
598
|
+
)
|
|
599
|
+
self.register_buffer("tap_select", tap_select, False)
|
|
577
600
|
|
|
578
601
|
def forward(self, x):
|
|
579
602
|
batch, chans, T, dim_in = x.shape
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
for i, blocks in enumerate(self.region_blocks.values()):
|
|
584
|
-
start, stop = self.region_bounds[i]
|
|
585
|
-
n_electrodes = stop - start
|
|
586
|
-
x_region = x[:, start:stop, :, :]
|
|
587
|
-
x_trans = (
|
|
588
|
-
x_region.permute(0, 2, 1, 3)
|
|
589
|
-
.reshape(-1, n_electrodes, dim_in)
|
|
590
|
-
.permute(0, 2, 1)
|
|
591
|
-
.unsqueeze(-1)
|
|
603
|
+
if len(self.region_bounds) == 0:
|
|
604
|
+
return torch.zeros(
|
|
605
|
+
(batch, chans, T, self.dim_out), device=x.device, dtype=x.dtype
|
|
592
606
|
)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
607
|
+
# (batch, chans, n_taps, T, dim_in): every electrode's ring neighbours
|
|
608
|
+
taps = torch.matmul(self.tap_select, x.reshape(batch, chans, T * dim_in))
|
|
609
|
+
taps = taps.reshape(batch, chans, -1, T, dim_in)
|
|
610
|
+
# Per kernel, one batched matmul over electrodes, each with its
|
|
611
|
+
# region's weights: (chans, batch * T, k * dim_in) @ (chans, k * dim_in, out)
|
|
612
|
+
weights: List[List[Tensor]] = [[] for _ in self.tap_starts]
|
|
613
|
+
biases: List[List[Tensor]] = [[] for _ in self.tap_starts]
|
|
614
|
+
for blocks in self.region_blocks.values():
|
|
615
|
+
for j, conv in enumerate(blocks):
|
|
616
|
+
bias = conv.bias
|
|
617
|
+
assert bias is not None
|
|
618
|
+
weights[j].append(conv.weight)
|
|
619
|
+
biases[j].append(bias)
|
|
620
|
+
outs = []
|
|
621
|
+
for j, start in enumerate(self.tap_starts):
|
|
622
|
+
w = torch.stack(weights[j]).index_select(0, self.chan_region)
|
|
623
|
+
k = w.shape[3]
|
|
624
|
+
w = w.squeeze(-1).permute(0, 3, 2, 1).reshape(chans, k * dim_in, -1)
|
|
625
|
+
t = taps[:, :, start : start + k].permute(1, 0, 3, 2, 4)
|
|
626
|
+
b = torch.stack(biases[j]).index_select(0, self.chan_region)
|
|
627
|
+
outs.append(
|
|
628
|
+
torch.bmm(t.reshape(chans, batch * T, k * dim_in), w) + b.unsqueeze(1)
|
|
610
629
|
)
|
|
611
|
-
|
|
612
|
-
|
|
630
|
+
out = torch.cat(outs, dim=-1).view(chans, batch, T, self.dim_out)
|
|
631
|
+
# contiguous: the residual sum keeps this layout and the next layer views it
|
|
632
|
+
return out.permute(1, 0, 2, 3).contiguous()
|
|
613
633
|
|
|
614
634
|
|
|
615
635
|
class _CSBrainEncoderLayer(nn.Module):
|
|
@@ -675,6 +695,17 @@ class _CSBrainEncoderLayer(nn.Module):
|
|
|
675
695
|
)
|
|
676
696
|
else:
|
|
677
697
|
self.region_attn_mask = None
|
|
698
|
+
# Region of every electrode and electrodes per region, for the
|
|
699
|
+
# region means of all regions at once (index_add_ sums in channel
|
|
700
|
+
# order, as the per-region mean did).
|
|
701
|
+
sizes = [stop - start for start, stop in self.region_bounds]
|
|
702
|
+
chan_region = [i for i, n in enumerate(sizes) for _ in range(n)]
|
|
703
|
+
self.register_buffer(
|
|
704
|
+
"chan_region", torch.tensor(chan_region, dtype=torch.long), False
|
|
705
|
+
)
|
|
706
|
+
self.register_buffer(
|
|
707
|
+
"region_sizes", torch.tensor(sizes, dtype=torch.long)[:, None], False
|
|
708
|
+
)
|
|
678
709
|
|
|
679
710
|
def forward(self, src: Tensor) -> Tensor:
|
|
680
711
|
x = src
|
|
@@ -711,11 +742,13 @@ class _CSBrainEncoderLayer(nn.Module):
|
|
|
711
742
|
def _inter_region_attention(self, x: Tensor) -> Tensor:
|
|
712
743
|
batch, chans, T, Fea = x.shape
|
|
713
744
|
x_flat = x.permute(0, 2, 1, 3).reshape(batch * T, chans, Fea)
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
745
|
+
if len(self.region_bounds) == 0:
|
|
746
|
+
global_features = torch.zeros_like(x_flat)
|
|
747
|
+
else:
|
|
748
|
+
region_sum = x_flat.new_zeros(batch * T, len(self.region_bounds), Fea)
|
|
749
|
+
region_sum.index_add_(1, self.chan_region, x_flat)
|
|
750
|
+
region_mean = region_sum / self.region_sizes
|
|
751
|
+
global_features = region_mean.index_select(1, self.chan_region)
|
|
719
752
|
x_enhanced = x_flat + self.global_fc(global_features)
|
|
720
753
|
|
|
721
754
|
attn_output = self.inter_region_attn(
|
{braindecode-1.8.1.dev174738705 → braindecode-1.8.1.dev174812945}/braindecode/models/diver1.py
RENAMED
|
@@ -18,7 +18,7 @@ import torch.nn.functional as F
|
|
|
18
18
|
from einops.layers.torch import Rearrange
|
|
19
19
|
from torch import nn
|
|
20
20
|
|
|
21
|
-
from braindecode.functional import rotate_pairs, spectral_input
|
|
21
|
+
from braindecode.functional import _real_dft, rotate_pairs, spectral_input
|
|
22
22
|
from braindecode.models.base import EEGModuleMixin
|
|
23
23
|
from braindecode.models.util import (
|
|
24
24
|
INTRACRANIAL_CH_TYPES,
|
|
@@ -397,10 +397,13 @@ class DIVER1(EEGModuleMixin, nn.Module, license="apache-2.0"):
|
|
|
397
397
|
tokens = self.patch_tokenizer(x)
|
|
398
398
|
tokens = self.patch_cnn(tokens)
|
|
399
399
|
if self.spectral_emb is not None:
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
400
|
+
if _real_dft.needs_real_dft(tokens):
|
|
401
|
+
amplitude = self._amplitude_real_dft(tokens).to(tokens)
|
|
402
|
+
else:
|
|
403
|
+
# At least float32, as the reference's float32 FFT.
|
|
404
|
+
fft_input = spectral_input(tokens)
|
|
405
|
+
spectrum = torch.fft.rfft(fft_input, dim=-1, norm="forward")
|
|
406
|
+
amplitude = spectrum.abs().to(tokens)
|
|
404
407
|
spectral_emb = self.spectral_emb(amplitude)
|
|
405
408
|
tokens = tokens + spectral_emb
|
|
406
409
|
if self.chan_emb is not None:
|
|
@@ -439,6 +442,16 @@ class DIVER1(EEGModuleMixin, nn.Module, license="apache-2.0"):
|
|
|
439
442
|
pooled = tokens.mean(dim=(1, 2))
|
|
440
443
|
return self.final_layer(pooled)
|
|
441
444
|
|
|
445
|
+
@torch.jit.unused
|
|
446
|
+
@_real_dft.fp32_island
|
|
447
|
+
def _amplitude_real_dft(self, tokens):
|
|
448
|
+
# |rfft(tokens, norm="forward")| on the device for accelerators without
|
|
449
|
+
# complex tensors (Intel Gaudi / HPU), instead of a host FFT. The norm
|
|
450
|
+
# keeps abs()'s zero gradient at an all-zero bin.
|
|
451
|
+
real, imag = _real_dft.real_rfft(tokens)
|
|
452
|
+
magnitude = torch.linalg.vector_norm(torch.stack((real, imag)), dim=0)
|
|
453
|
+
return magnitude / tokens.shape[-1]
|
|
454
|
+
|
|
442
455
|
|
|
443
456
|
class _PatchCNN(nn.Sequential):
|
|
444
457
|
"""Power-of-two padded patches encoded by Conv2d/GroupNorm/GELU.
|
|
@@ -618,28 +631,13 @@ class _STCPE(nn.Module):
|
|
|
618
631
|
inner_heads -= 1
|
|
619
632
|
self.window = window
|
|
620
633
|
self.n_patches = n_patches
|
|
621
|
-
# Full-height windows, padded in time to include partial overlaps
|
|
622
|
-
|
|
623
|
-
|
|
634
|
+
# Full-height windows, padded in time to include partial overlaps:
|
|
635
|
+
# window w holds patches w - (window - 1) .. w. Entry j * n_windows + w
|
|
636
|
+
# is the padded patch index of offset j in window w (offset outermost,
|
|
637
|
+
# the order in which F.unfold / F.fold accumulate).
|
|
624
638
|
self.n_windows = n_patches + window - 1
|
|
625
|
-
|
|
626
|
-
self.
|
|
627
|
-
"batch chan patch dim -> (batch dim) 1 chan patch"
|
|
628
|
-
)
|
|
629
|
-
self.unfold_features = Rearrange(
|
|
630
|
-
"(batch dim) 1 chan patch -> batch chan patch dim", dim=inner_dim
|
|
631
|
-
)
|
|
632
|
-
# One sequence per window for the inner encoder, and back again.
|
|
633
|
-
self.windows_to_batch = Rearrange(
|
|
634
|
-
"(batch dim) (chan window) n_win -> (batch n_win) (chan window) dim",
|
|
635
|
-
dim=inner_dim,
|
|
636
|
-
window=window,
|
|
637
|
-
)
|
|
638
|
-
self.batch_to_windows = Rearrange(
|
|
639
|
-
"(batch n_win) (chan window) dim -> (batch dim) (chan window) n_win",
|
|
640
|
-
n_win=self.n_windows,
|
|
641
|
-
window=window,
|
|
642
|
-
)
|
|
639
|
+
offsets = torch.arange(window)[:, None] + torch.arange(self.n_windows)
|
|
640
|
+
self.register_buffer("window_index", offsets.flatten(), persistent=False)
|
|
643
641
|
self.down = nn.Linear(d_model, inner_dim)
|
|
644
642
|
self.encoder = _AnyVariateEncoder(
|
|
645
643
|
d_model=inner_dim,
|
|
@@ -655,49 +653,43 @@ class _STCPE(nn.Module):
|
|
|
655
653
|
|
|
656
654
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
657
655
|
"""Map a token grid to a positional bias of the same shape."""
|
|
658
|
-
n_chans = x.shape
|
|
659
|
-
|
|
660
|
-
grid_size = (n_chans, self.n_patches)
|
|
656
|
+
batch, n_chans, n_patches, _ = x.shape
|
|
657
|
+
window, n_windows = self.window, self.n_windows
|
|
661
658
|
|
|
662
659
|
x = self.down(x)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
660
|
+
dim = x.shape[-1]
|
|
661
|
+
# Every window gathered at once (a strided unfold and its fold-shaped
|
|
662
|
+
# backward are slow on some accelerators, e.g. Intel Gaudi).
|
|
663
|
+
windows = (
|
|
664
|
+
F.pad(x, (0, 0, window - 1, window - 1))
|
|
665
|
+
.index_select(2, self.window_index)
|
|
666
|
+
.view(batch, n_chans, window, n_windows, dim)
|
|
667
|
+
.permute(0, 3, 1, 2, 4)
|
|
668
|
+
.reshape(batch * n_windows, n_chans * window, dim)
|
|
669
669
|
)
|
|
670
|
-
windows = self.windows_to_batch(unfolded)
|
|
671
670
|
|
|
672
671
|
var_id = torch.arange(n_chans, device=x.device).repeat_interleave(self.window)
|
|
673
672
|
time_id = torch.arange(self.window, device=x.device).repeat(n_chans)
|
|
674
673
|
encoded = self.encoder(windows, var_id=var_id, time_id=time_id)
|
|
675
|
-
encoded =
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
674
|
+
encoded = (
|
|
675
|
+
encoded.view(batch, n_windows, n_chans, window, dim)
|
|
676
|
+
.permute(0, 2, 3, 1, 4)
|
|
677
|
+
.reshape(batch, n_chans, window * n_windows, dim)
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
# Overlap-add back onto the padded patches, then drop the padding.
|
|
681
|
+
n_padded = n_patches + 2 * (window - 1)
|
|
682
|
+
folded = encoded.new_zeros(batch, n_chans, n_padded, dim).index_add(
|
|
683
|
+
2, self.window_index, encoded
|
|
683
684
|
)
|
|
684
|
-
#
|
|
685
|
+
# Count in the working dtype: a scalar window divisor changes BF16
|
|
685
686
|
# accumulation for large windows (e.g. 259 overlaps accumulate to 256).
|
|
686
|
-
overlap =
|
|
687
|
-
|
|
688
|
-
1,
|
|
689
|
-
n_chans * self.window,
|
|
690
|
-
self.n_windows,
|
|
691
|
-
dtype=x.dtype,
|
|
692
|
-
device=x.device,
|
|
693
|
-
),
|
|
694
|
-
output_size=grid_size,
|
|
695
|
-
kernel_size=kernel_size,
|
|
696
|
-
stride=self.stride,
|
|
697
|
-
padding=self.padding,
|
|
687
|
+
overlap = x.new_zeros(n_padded).index_add(
|
|
688
|
+
0, self.window_index, x.new_ones(window * n_windows)
|
|
698
689
|
)
|
|
699
|
-
|
|
700
|
-
|
|
690
|
+
start, stop = window - 1, window - 1 + n_patches
|
|
691
|
+
averaged = folded[:, :, start:stop] / overlap[start:stop, None]
|
|
692
|
+
return self.up(averaged)
|
|
701
693
|
|
|
702
694
|
|
|
703
695
|
class _AnyVariateEncoder(nn.Module):
|