Braindecode 1.8.1.dev183301946__tar.gz → 1.8.1.dev183301950__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.dev183301946 → braindecode-1.8.1.dev183301950}/NOTICE.txt +1 -0
- {braindecode-1.8.1.dev183301946/braindecode.egg-info → braindecode-1.8.1.dev183301950}/PKG-INFO +1 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/base.py +57 -5
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/functional/_real_dft.py +55 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/functional/functions.py +32 -13
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/__init__.py +2 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/axon.py +42 -8
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/barista.py +2 -2
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/brainmodule.py +13 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/codebrain.py +42 -18
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/contrawr.py +23 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/csbrain.py +69 -36
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/ctnet.py +9 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/diver1.py +51 -59
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegconformer.py +14 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eeginception_mi.py +7 -3
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegnex.py +7 -3
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegpt.py +15 -9
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegsym.py +68 -16
- braindecode-1.8.1.dev183301950/braindecode/models/epint.py +311 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/labram.py +34 -15
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/luna.py +7 -4
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/medformer.py +47 -20
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/meta_neuromotor.py +35 -8
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/mirepnet.py +15 -4
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/msvtnet.py +115 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/neurorvq.py +3 -2
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/neurorvq_tokenizer.py +10 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/patchedtransformer.py +3 -7
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/reve.py +16 -4
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/signal_jepa.py +81 -3
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/sinc_shallow.py +2 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/summary.csv +1 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/tfm_tokenizer.py +44 -3
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/util.py +2 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/zuna.py +11 -9
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/attention.py +87 -6
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/dance_modules.py +16 -3
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/filter.py +55 -21
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/layers.py +41 -2
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/samplers/base.py +60 -61
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/training/losses.py +5 -4
- braindecode-1.8.1.dev183301950/braindecode/version.py +1 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950/braindecode.egg-info}/PKG-INFO +1 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode.egg-info/SOURCES.txt +1 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/api.rst +1 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/whats_new.rst +68 -0
- braindecode-1.8.1.dev183301946/braindecode/version.py +0 -1
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/LICENSE.txt +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/MANIFEST.in +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/README.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/augmentation/base.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/augmentation/functional.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/augmentation/transforms.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/classifier.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/_notebook_viewer.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/datasets.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/format.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/hub.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/collate.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datautil/serialization.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/datautil/util.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/eegneuralnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/functional/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/atcnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/base.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/bendr.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/biot.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/brainbert.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/brainomni.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/brant.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/config.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/dance.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/deep4.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eeg_clip.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegdino.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eeginception_erp.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegitnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegminer.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/emg2qwerty.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/fbmsnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/guetschel2026.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/mapa.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/mscformer.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/mvpformer.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/neuropose.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/popt.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/seizure_transformer.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/sensingdynamics.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/shallow_fbcsp.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/sleepfm.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/steegformer.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/tcformer.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/tcn.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/tmsanet.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/usleep.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/vemg2pose.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/activation.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/blocks.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/channels.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/convolution.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/linear.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/linear_attention.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/parametrization.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/popt_modules.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/quantization.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/stats.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/util.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/mne_preprocess.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/preprocess.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/windowers.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/regressor.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/training/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/training/scoring.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/util.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/visualization/activations.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/visualization/attribution.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/visualization/frequency.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/visualization/metrics.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/visualization/sanity.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/visualization/topology.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode.egg-info/requires.txt +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/Makefile +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/agent_docs.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/cite.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/conf.py +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/help.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/index.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/install/install.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/install/install_pip.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/install/install_source.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/models.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/models_table.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/docs/user_guide/channel_strategies.rst +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/pyproject.toml +0 -0
- {braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/setup.cfg +0 -0
|
@@ -54,6 +54,7 @@ The following components are licensed under the MIT License:
|
|
|
54
54
|
- `docs/_static/model/seizure_transformer_arch.png`
|
|
55
55
|
- `braindecode/models/guetschel2026.py`
|
|
56
56
|
- `braindecode/models/tmsanet.py`
|
|
57
|
+
- `braindecode/models/epint.py`
|
|
57
58
|
|
|
58
59
|
The `brainomni.py` file is a derivative of `OpenTSLab/BrainOmni` (MIT), whose
|
|
59
60
|
SEANet codec derives from Meta's `facebookresearch/encodec` (MIT). The
|
{braindecode-1.8.1.dev183301946/braindecode.egg-info → braindecode-1.8.1.dev183301950}/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.dev183301950
|
|
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.dev183301946 → braindecode-1.8.1.dev183301950}/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
|
"""
|
|
@@ -1475,7 +1518,9 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1475
1518
|
metadata : pd.DataFrame
|
|
1476
1519
|
DataFrame containing as many rows as there are windows in the
|
|
1477
1520
|
BaseConcatDataset, with the metadata and description information
|
|
1478
|
-
for each window
|
|
1521
|
+
for each window, and an ``i_dataset`` column giving the position
|
|
1522
|
+
in ``datasets`` of the dataset (recording) each window comes from.
|
|
1523
|
+
Samplers use it to keep recordings apart.
|
|
1479
1524
|
"""
|
|
1480
1525
|
if not all(
|
|
1481
1526
|
[
|
|
@@ -1501,9 +1546,15 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1501
1546
|
"Dataset descriptions cannot contain the reserved window "
|
|
1502
1547
|
"metadata key 'i_trial_in_dataset'."
|
|
1503
1548
|
)
|
|
1549
|
+
if "i_dataset" in df.columns or "i_dataset" in ds.description:
|
|
1550
|
+
raise ValueError(
|
|
1551
|
+
"Window metadata and dataset descriptions cannot contain the "
|
|
1552
|
+
"key 'i_dataset', which get_metadata() reserves for the "
|
|
1553
|
+
"position of the dataset each window comes from. Rename it."
|
|
1554
|
+
)
|
|
1504
1555
|
|
|
1505
1556
|
all_dfs = list()
|
|
1506
|
-
for ds in self.datasets:
|
|
1557
|
+
for i_ds, ds in enumerate(self.datasets):
|
|
1507
1558
|
if hasattr(ds, "_windows") and ds._windows is not None:
|
|
1508
1559
|
df = ds._windows.metadata
|
|
1509
1560
|
else:
|
|
@@ -1513,6 +1564,7 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
|
|
|
1513
1564
|
df = df.copy()
|
|
1514
1565
|
for k, v in ds.description.items():
|
|
1515
1566
|
df[k] = v
|
|
1567
|
+
df["i_dataset"] = i_ds
|
|
1516
1568
|
all_dfs.append(df)
|
|
1517
1569
|
|
|
1518
1570
|
return pd.concat(all_dfs)
|
|
@@ -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,22 +83,35 @@ 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
|
|
88
91
|
n_fft = 2 * _next_fast_len((n_times + kernel_size) // 2)
|
|
89
92
|
xs = spectral_input(x)
|
|
90
93
|
# conv1d is a cross-correlation: a convolution with the flipped kernel
|
|
91
|
-
x_f = torch.fft.rfft(xs, n=n_fft)
|
|
94
|
+
x_f = torch.fft.rfft(xs, n=n_fft) # (batch, in, freq)
|
|
92
95
|
w_f = torch.fft.rfft(weight.to(xs).flip(-1), n=n_fft).permute(2, 1, 0)
|
|
93
96
|
# Per frequency (batch, in) @ (in, out) in complex numbers, as one real
|
|
94
|
-
# matmul (a complex one is slower on CPU)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
# matmul (a complex one is slower on CPU).
|
|
98
|
+
needs_grad = torch.is_grad_enabled() and (x.requires_grad or weight.requires_grad)
|
|
99
|
+
if needs_grad or x.shape[0] >= 2 * x.shape[1]:
|
|
100
|
+
# [Re x, Im x] @ [[Re w, Im w], [-Im w, Re w]] (rows of i * w): the
|
|
101
|
+
# output columns are the (Re, Im) pairs of the result, read as complex
|
|
102
|
+
# without a copy (and without a costly backward).
|
|
103
|
+
w_mat = torch.view_as_real(torch.stack([w_f, w_f * 1j], 1)).flatten(1, 2)
|
|
104
|
+
y_f = torch.view_as_real(x_f).permute(2, 0, 3, 1).flatten(2) @ w_mat.flatten(2)
|
|
105
|
+
y_f = torch.view_as_complex(y_f.unflatten(-1, [-1, 2]))
|
|
106
|
+
else:
|
|
107
|
+
# Forward only with fewer rows than twice the input channels: the
|
|
108
|
+
# doubled weight costs more than combining [Re x; Im x] @ [Re w, Im w].
|
|
109
|
+
x_f = x_f.permute(2, 0, 1)
|
|
110
|
+
p = torch.cat([x_f.real, x_f.imag], 1) @ torch.view_as_real(w_f).flatten(2)
|
|
111
|
+
p = p.unflatten(1, [2, x.shape[0]]).unflatten(3, [-1, 2])
|
|
112
|
+
y_f = torch.complex(
|
|
113
|
+
p[:, 0, :, :, 0] - p[:, 1, :, :, 1], p[:, 0, :, :, 1] + p[:, 1, :, :, 0]
|
|
114
|
+
)
|
|
101
115
|
y = torch.fft.irfft(y_f.permute(1, 2, 0), n=n_fft)
|
|
102
116
|
# "same" output t is sample t + kernel_size // 2 of the full convolution
|
|
103
117
|
y = y[..., kernel_size // 2 : kernel_size // 2 + n_times]
|
|
@@ -115,8 +129,11 @@ def prefer_fft_conv(x: torch.Tensor, kernel_size: int) -> bool:
|
|
|
115
129
|
and from ``batch_size * kernel_size >= 600`` if also
|
|
116
130
|
``in_channels * kernel_size >= 120``: with one or two input channels a
|
|
117
131
|
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
|
-
|
|
132
|
+
temporal convolutions of the model zoo, 22 channels x batch 32). On Intel
|
|
133
|
+
Gaudi (HPU) a direct convolution costs about ``batch_size * n_times *
|
|
134
|
+
kernel_size`` and the FFT about a fixed few milliseconds in eager mode: the
|
|
135
|
+
FFT wins from 8 input channels and ``batch_size * n_times * kernel_size >=
|
|
136
|
+
8e6``. GPUs keep the direct convolution.
|
|
120
137
|
|
|
121
138
|
Parameters
|
|
122
139
|
----------
|
|
@@ -125,6 +142,8 @@ def prefer_fft_conv(x: torch.Tensor, kernel_size: int) -> bool:
|
|
|
125
142
|
kernel_size : int
|
|
126
143
|
Kernel length in samples.
|
|
127
144
|
"""
|
|
145
|
+
if x.device.type == "hpu":
|
|
146
|
+
return x.shape[1] >= 8 and x.shape[0] * x.shape[-1] * kernel_size >= 8e6
|
|
128
147
|
return x.device.type == "cpu" and (
|
|
129
148
|
x.element_size() == 2
|
|
130
149
|
or (x.shape[0] * kernel_size >= 600 and x.shape[1] * kernel_size >= 120)
|
{braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/__init__.py
RENAMED
|
@@ -36,6 +36,7 @@ from .eegsimpleconv import EEGSimpleConv
|
|
|
36
36
|
from .eegsym import EEGSym
|
|
37
37
|
from .eegtcnet import EEGTCNet
|
|
38
38
|
from .emg2qwerty import EMG2QwertyNet
|
|
39
|
+
from .epint import EpiNT
|
|
39
40
|
from .fbcnet import FBCNet
|
|
40
41
|
from .fblightconvnet import FBLightConvNet
|
|
41
42
|
from .fbmsnet import FBMSNet
|
|
@@ -134,6 +135,7 @@ __all__ = [
|
|
|
134
135
|
"EEGTCNet",
|
|
135
136
|
"DGCNN",
|
|
136
137
|
"EMG2QwertyNet",
|
|
138
|
+
"EpiNT",
|
|
137
139
|
"NeuroPose",
|
|
138
140
|
"SensingDynamics",
|
|
139
141
|
"VEMG2Pose",
|
{braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/axon.py
RENAMED
|
@@ -14,6 +14,7 @@ from torch import nn
|
|
|
14
14
|
from braindecode.functional import sinusoidal_positional_encoding
|
|
15
15
|
from braindecode.models.base import EEGModuleMixin
|
|
16
16
|
from braindecode.models.util import warn_if_sfreq_differs
|
|
17
|
+
from braindecode.modules.attention import _scaled_dot_product_attention
|
|
17
18
|
from braindecode.modules.channels import _resolve
|
|
18
19
|
|
|
19
20
|
|
|
@@ -462,11 +463,33 @@ class _AxisAttention(nn.Module):
|
|
|
462
463
|
.unbind(0)
|
|
463
464
|
)
|
|
464
465
|
drop = self.att_drop_prob if self.training else 0.0
|
|
465
|
-
attended =
|
|
466
|
+
attended = _scaled_dot_product_attention(
|
|
466
467
|
query, key, value, attn_mask=mask, dropout_p=drop
|
|
467
468
|
)
|
|
468
469
|
return attended.transpose(1, 2).reshape(n_seq, length, -1)
|
|
469
470
|
|
|
471
|
+
def _attend_two_scales(
|
|
472
|
+
self, projected: torch.Tensor, band: torch.Tensor, weights: torch.Tensor
|
|
473
|
+
) -> torch.Tensor:
|
|
474
|
+
"""Banded and full attention from one score matrix, mixed per query.
|
|
475
|
+
|
|
476
|
+
``weights`` (N, L, 2) are the scale-gate weights. Both softmaxes share
|
|
477
|
+
the scores and one product with the values serves the mixture.
|
|
478
|
+
"""
|
|
479
|
+
n_seq, length, _ = projected.shape
|
|
480
|
+
query, key, value = (
|
|
481
|
+
projected.reshape(n_seq, length, 3, self.num_heads, self.head_dim)
|
|
482
|
+
.permute(2, 0, 3, 1, 4)
|
|
483
|
+
.unbind(0)
|
|
484
|
+
)
|
|
485
|
+
scores = torch.matmul(query, key.transpose(-2, -1)) * self.head_dim**-0.5
|
|
486
|
+
weights = weights.reshape(n_seq, 1, length, 2)
|
|
487
|
+
probs = weights[..., 0:1] * scores.masked_fill(~band, float("-inf")).softmax(
|
|
488
|
+
-1
|
|
489
|
+
) + weights[..., 1:2] * scores.softmax(-1)
|
|
490
|
+
attended = torch.matmul(probs, value)
|
|
491
|
+
return attended.transpose(1, 2).reshape(n_seq, length, -1)
|
|
492
|
+
|
|
470
493
|
def forward(self, x: torch.Tensor, band: Optional[torch.Tensor]) -> torch.Tensor:
|
|
471
494
|
batch, n_chans, n_patches, dim = x.shape
|
|
472
495
|
axis_weights = self.axis_gate(x) # (B, C, P, 2)
|
|
@@ -476,15 +499,26 @@ class _AxisAttention(nn.Module):
|
|
|
476
499
|
# the restricted branch is the full one; attend once unless the two
|
|
477
500
|
# branches draw their own attention dropout.
|
|
478
501
|
per_channel = self.temporal_qkv(x.reshape(batch * n_chans, n_patches, dim))
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
502
|
+
dropout = self.training and self.att_drop_prob > 0
|
|
503
|
+
if band is not None and not dropout:
|
|
504
|
+
# The scale-gate weights sum to one, so the two branches share the
|
|
505
|
+
# scores, the product with the values and temporal_out.
|
|
506
|
+
attended = self._attend_two_scales(per_channel, band, scale_weights)
|
|
507
|
+
temporal = self.temporal_out(attended).reshape(
|
|
485
508
|
batch, n_chans, n_patches, dim
|
|
486
509
|
)
|
|
487
|
-
|
|
510
|
+
else:
|
|
511
|
+
restricted = self.temporal_out(self._attend(per_channel, band)).reshape(
|
|
512
|
+
batch, n_chans, n_patches, dim
|
|
513
|
+
)
|
|
514
|
+
full = restricted
|
|
515
|
+
if dropout:
|
|
516
|
+
full = self.temporal_out(self._attend(per_channel, None)).reshape(
|
|
517
|
+
batch, n_chans, n_patches, dim
|
|
518
|
+
)
|
|
519
|
+
temporal = (
|
|
520
|
+
scale_weights[..., 0:1] * restricted + scale_weights[..., 1:2] * full
|
|
521
|
+
)
|
|
488
522
|
|
|
489
523
|
# Spatial path: one sequence of C tokens per time step.
|
|
490
524
|
per_step = self.spatial_qkv(
|
{braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/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.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/brainmodule.py
RENAMED
|
@@ -20,6 +20,7 @@ from torch.nn import functional as F
|
|
|
20
20
|
from braindecode.models.base import EEGModuleMixin
|
|
21
21
|
from braindecode.models.util import has_valid_locations, positions_from_chs_info
|
|
22
22
|
from braindecode.modules import ChannelMerger
|
|
23
|
+
from braindecode.modules.attention import _hpu_lazy
|
|
23
24
|
from braindecode.modules.layers import SubjectLayers
|
|
24
25
|
|
|
25
26
|
__all__ = ["BrainModule"]
|
|
@@ -712,7 +713,7 @@ class _ConvSequence(nn.Module):
|
|
|
712
713
|
nn.Conv1d(
|
|
713
714
|
chout, 2 * chout, 1 + 2 * glu_context, padding=glu_context
|
|
714
715
|
),
|
|
715
|
-
|
|
716
|
+
_GLU(dim=1),
|
|
716
717
|
)
|
|
717
718
|
)
|
|
718
719
|
else:
|
|
@@ -727,6 +728,17 @@ class _ConvSequence(nn.Module):
|
|
|
727
728
|
return x
|
|
728
729
|
|
|
729
730
|
|
|
731
|
+
class _GLU(nn.GLU):
|
|
732
|
+
"""``nn.GLU`` that Intel Gaudi's lazy mode can compile."""
|
|
733
|
+
|
|
734
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
735
|
+
if not torch.jit.is_scripting() and x.device.type == "hpu" and _hpu_lazy():
|
|
736
|
+
# Its graph compiler fails on ``aten.glu`` after a convolution.
|
|
737
|
+
a, b = x.chunk(2, dim=self.dim)
|
|
738
|
+
return a * torch.sigmoid(b)
|
|
739
|
+
return F.glu(x, self.dim)
|
|
740
|
+
|
|
741
|
+
|
|
730
742
|
def _pad_multiple(x: torch.Tensor, base: int) -> torch.Tensor:
|
|
731
743
|
"""Pad tensor to be a multiple of base."""
|
|
732
744
|
length = x.shape[-1]
|
{braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/braindecode/models/codebrain.py
RENAMED
|
@@ -15,7 +15,9 @@ 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
|
|
20
|
+
from braindecode.modules.attention import _hpu_lazy
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class CodeBrain(EEGModuleMixin, nn.Module):
|
|
@@ -521,16 +523,21 @@ class _GConv(nn.Module):
|
|
|
521
523
|
kernel = F.pad(k_fwd, (0, seq_len)) + F.pad(k_bwd.flip(-1), (seq_len, 0))
|
|
522
524
|
|
|
523
525
|
# FFT-based convolution: O(N log N)
|
|
526
|
+
# The bidirectional kernel is circular (lags 0..L-1, then -L..-1).
|
|
527
|
+
# Rolled to lags -L..L-1 it is a linear convolution read from lag 0,
|
|
528
|
+
# so any FFT length >= 2 * seq_len works: take a 5-smooth (fast) one.
|
|
529
|
+
start = seq_len if self.bidirectional else 0
|
|
530
|
+
n_fft = 2 * _next_fast_len(seq_len)
|
|
524
531
|
# kernel_freq: (channels, d_model, freq_bins)
|
|
525
532
|
# spectral_input(x) dtype: x's, at least float32 (the reference's .float()).
|
|
526
533
|
x_in = spectral_input(x)
|
|
527
|
-
kernel_freq = torch.fft.rfft(kernel.to(x_in), n=
|
|
534
|
+
kernel_freq = torch.fft.rfft(kernel.roll(start, -1).to(x_in), n=n_fft)
|
|
528
535
|
# x_freq: (batch, d_model, freq_bins)
|
|
529
|
-
x_freq = torch.fft.rfft(x_in, n=
|
|
536
|
+
x_freq = torch.fft.rfft(x_in, n=n_fft)
|
|
530
537
|
# out_freq: (batch, channels, d_model, freq_bins)
|
|
531
538
|
out_freq = torch.einsum("bhl,chl->bchl", x_freq, kernel_freq)
|
|
532
539
|
# out: (batch, channels, d_model, seq_len)
|
|
533
|
-
out = torch.fft.irfft(out_freq, n=
|
|
540
|
+
out = torch.fft.irfft(out_freq, n=n_fft)[..., start : start + seq_len].to(x)
|
|
534
541
|
|
|
535
542
|
# Skip connection via learnable D matrix
|
|
536
543
|
# (batch, channels, d_model, seq_len)
|
|
@@ -697,18 +704,15 @@ class _ResidualBlock(nn.Module):
|
|
|
697
704
|
# Sliding-window attention branch
|
|
698
705
|
# (batch, 2*res_channels, seq_len) -> (batch, seq_len, 2*res_channels)
|
|
699
706
|
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
|
-
):
|
|
707
|
+
if self.swa_window_size == 1 and h_attn.device.type == "cpu":
|
|
706
708
|
# 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
|
-
#
|
|
709
|
+
# exactly 1 and 0, so the attention returns its value projection,
|
|
710
|
+
# times the weights' dropout in training (the same (batch * heads,
|
|
711
|
+
# L, L) mask nn.MultiheadAttention draws, of which only the
|
|
712
|
+
# diagonal reaches the output). Same projection calls as
|
|
713
|
+
# nn.MultiheadAttention (on its (seq, batch, embed) view), so
|
|
714
|
+
# outputs and gradients are unchanged bit for bit, and so is the
|
|
715
|
+
# RNG stream. (On Gaudi the full attention was faster.)
|
|
712
716
|
# ponytail: the unused query/key projections are computed too, to
|
|
713
717
|
# keep the rounding; in_proj_weight[2E:] alone is ~2x less GEMM.
|
|
714
718
|
embed_dim = self.attention.embed_dim
|
|
@@ -716,6 +720,16 @@ class _ResidualBlock(nn.Module):
|
|
|
716
720
|
value = F.linear(
|
|
717
721
|
seq_first, self.attention.in_proj_weight, self.attention.in_proj_bias
|
|
718
722
|
)[..., 2 * embed_dim :]
|
|
723
|
+
p = self.attention.dropout
|
|
724
|
+
if self.training and p > 0.0:
|
|
725
|
+
n_rows = seq_first.shape[1] * self.attention.num_heads
|
|
726
|
+
keep = (
|
|
727
|
+
value.new_empty(n_rows, seq_len, seq_len)
|
|
728
|
+
.bernoulli_(1 - p)
|
|
729
|
+
.diagonal(dim1=1, dim2=2)
|
|
730
|
+
.div(1 - p)
|
|
731
|
+
)
|
|
732
|
+
value = value.reshape(seq_len, n_rows, -1) * keep.t().unsqueeze(-1)
|
|
719
733
|
h_attn = (
|
|
720
734
|
self.attention.out_proj(value.reshape(-1, embed_dim))
|
|
721
735
|
.view(seq_first.shape)
|
|
@@ -818,10 +832,20 @@ class _ResidualGroup(nn.Module):
|
|
|
818
832
|
def forward(self, input_data: torch.Tensor) -> torch.Tensor:
|
|
819
833
|
noise = input_data
|
|
820
834
|
h = noise
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
835
|
+
if not torch.jit.is_scripting() and h.device.type == "hpu" and _hpu_lazy():
|
|
836
|
+
# Gaudi's lazy-mode graph compiler fails on the backward of the
|
|
837
|
+
# sequential skip sum (one skip_conv dedw node lands in two
|
|
838
|
+
# pipeline bundles); one n-ary stack-sum reduction compiles.
|
|
839
|
+
skip_list = []
|
|
840
|
+
for block in self.residual_blocks:
|
|
841
|
+
h, skip_n = block((h, noise))
|
|
842
|
+
skip_list.append(skip_n)
|
|
843
|
+
skip = torch.stack(skip_list, dim=0).sum(dim=0)
|
|
844
|
+
else:
|
|
845
|
+
skip = torch.zeros((), dtype=h.dtype, device=h.device)
|
|
846
|
+
for block in self.residual_blocks:
|
|
847
|
+
h, skip_n = block((h, noise))
|
|
848
|
+
skip = skip_n + skip
|
|
825
849
|
|
|
826
850
|
return skip * math.sqrt(1.0 / self.num_res_layers)
|
|
827
851
|
|
{braindecode-1.8.1.dev183301946 → braindecode-1.8.1.dev183301950}/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)
|