braindecode 1.8.1.dev183280391__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.
Files changed (209) hide show
  1. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/NOTICE.txt +1 -0
  2. {braindecode-1.8.1.dev183280391/braindecode.egg-info → braindecode-1.8.1.dev183301950}/PKG-INFO +1 -1
  3. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/base.py +11 -2
  4. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/functional/functions.py +19 -8
  5. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/__init__.py +2 -0
  6. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/axon.py +42 -8
  7. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/brainmodule.py +13 -1
  8. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/codebrain.py +15 -4
  9. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/ctnet.py +9 -1
  10. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegconformer.py +14 -1
  11. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegnex.py +6 -2
  12. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegpt.py +12 -8
  13. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegsym.py +68 -16
  14. braindecode-1.8.1.dev183301950/braindecode/models/epint.py +311 -0
  15. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/luna.py +4 -3
  16. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/medformer.py +47 -20
  17. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/mirepnet.py +15 -4
  18. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/msvtnet.py +115 -0
  19. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/neurorvq.py +3 -2
  20. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/patchedtransformer.py +3 -7
  21. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/reve.py +3 -2
  22. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/signal_jepa.py +81 -3
  23. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/sinc_shallow.py +2 -1
  24. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/summary.csv +1 -0
  25. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/tfm_tokenizer.py +44 -3
  26. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/util.py +2 -0
  27. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/zuna.py +9 -8
  28. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/attention.py +47 -5
  29. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/layers.py +34 -1
  30. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/samplers/base.py +60 -61
  31. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/training/losses.py +5 -4
  32. braindecode-1.8.1.dev183301950/braindecode/version.py +1 -0
  33. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950/braindecode.egg-info}/PKG-INFO +1 -1
  34. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode.egg-info/SOURCES.txt +1 -0
  35. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/api.rst +1 -0
  36. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/whats_new.rst +41 -0
  37. braindecode-1.8.1.dev183280391/braindecode/version.py +0 -1
  38. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/LICENSE.txt +0 -0
  39. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/MANIFEST.in +0 -0
  40. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/README.rst +0 -0
  41. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/__init__.py +0 -0
  42. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/augmentation/__init__.py +0 -0
  43. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/augmentation/base.py +0 -0
  44. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/augmentation/functional.py +0 -0
  45. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/augmentation/transforms.py +0 -0
  46. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/classifier.py +0 -0
  47. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/__init__.py +0 -0
  48. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/_notebook_viewer.py +0 -0
  49. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bbci.py +0 -0
  50. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bcicomp.py +0 -0
  51. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/__init__.py +0 -0
  52. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/datasets.py +0 -0
  53. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/format.py +0 -0
  54. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/hub.py +0 -0
  55. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/hub_format.py +0 -0
  56. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/hub_io.py +0 -0
  57. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/hub_validation.py +0 -0
  58. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/bids/iterable.py +0 -0
  59. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/chb_mit.py +0 -0
  60. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/collate.py +0 -0
  61. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/mne.py +0 -0
  62. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/moabb.py +0 -0
  63. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/nmt.py +0 -0
  64. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/registry.py +0 -0
  65. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/siena.py +0 -0
  66. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
  67. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/sleep_physionet.py +0 -0
  68. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/tuh.py +0 -0
  69. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/utils.py +0 -0
  70. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datasets/xy.py +0 -0
  71. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datautil/__init__.py +0 -0
  72. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datautil/channel_utils.py +0 -0
  73. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datautil/hub_formats.py +0 -0
  74. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datautil/serialization.py +0 -0
  75. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/datautil/util.py +0 -0
  76. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/eegneuralnet.py +0 -0
  77. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/functional/__init__.py +0 -0
  78. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/functional/_real_dft.py +0 -0
  79. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/functional/initialization.py +0 -0
  80. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/atcnet.py +0 -0
  81. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/attentionbasenet.py +0 -0
  82. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/attn_sleep.py +0 -0
  83. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/barista.py +0 -0
  84. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/base.py +0 -0
  85. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/bendr.py +0 -0
  86. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/biot.py +0 -0
  87. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/brainbert.py +0 -0
  88. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/brainomni.py +0 -0
  89. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/brant.py +0 -0
  90. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/cbramod.py +0 -0
  91. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/config.py +0 -0
  92. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/contrawr.py +0 -0
  93. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/csbrain.py +0 -0
  94. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/dance.py +0 -0
  95. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/deep4.py +0 -0
  96. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/deepsleepnet.py +0 -0
  97. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/dgcnn.py +0 -0
  98. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/diver1.py +0 -0
  99. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eeg_clip.py +0 -0
  100. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegdino.py +0 -0
  101. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eeginception_erp.py +0 -0
  102. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eeginception_mi.py +0 -0
  103. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegitnet.py +0 -0
  104. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegminer.py +0 -0
  105. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegnet.py +0 -0
  106. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegsimpleconv.py +0 -0
  107. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/eegtcnet.py +0 -0
  108. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/emg2qwerty.py +0 -0
  109. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/fbcnet.py +0 -0
  110. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/fblightconvnet.py +0 -0
  111. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/fbmsnet.py +0 -0
  112. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/guetschel2026.py +0 -0
  113. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/hybrid.py +0 -0
  114. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/ifnet.py +0 -0
  115. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/labram.py +0 -0
  116. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/mapa.py +0 -0
  117. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/meta_neuromotor.py +0 -0
  118. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/mscformer.py +0 -0
  119. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/mvpformer.py +0 -0
  120. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/neuropose.py +0 -0
  121. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/neurorvq_tokenizer.py +0 -0
  122. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/popt.py +0 -0
  123. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/sccnet.py +0 -0
  124. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/seizure_transformer.py +0 -0
  125. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/sensingdynamics.py +0 -0
  126. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/shallow_fbcsp.py +0 -0
  127. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
  128. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
  129. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/sleepfm.py +0 -0
  130. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/sparcnet.py +0 -0
  131. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/sstdpn.py +0 -0
  132. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/steegformer.py +0 -0
  133. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/syncnet.py +0 -0
  134. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/tcformer.py +0 -0
  135. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/tcn.py +0 -0
  136. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/tidnet.py +0 -0
  137. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/tmsanet.py +0 -0
  138. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/tsinception.py +0 -0
  139. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/usleep.py +0 -0
  140. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/models/vemg2pose.py +0 -0
  141. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/__init__.py +0 -0
  142. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/activation.py +0 -0
  143. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/blocks.py +0 -0
  144. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/channels.py +0 -0
  145. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/convolution.py +0 -0
  146. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/dance_modules.py +0 -0
  147. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/filter.py +0 -0
  148. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/linear.py +0 -0
  149. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/linear_attention.py +0 -0
  150. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/parametrization.py +0 -0
  151. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/popt_modules.py +0 -0
  152. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/quantization.py +0 -0
  153. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/stats.py +0 -0
  154. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/util.py +0 -0
  155. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/modules/wrapper.py +0 -0
  156. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/__init__.py +0 -0
  157. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
  158. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/mne_preprocess.py +0 -0
  159. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/preprocess.py +0 -0
  160. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/util.py +0 -0
  161. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/preprocessing/windowers.py +0 -0
  162. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/regressor.py +0 -0
  163. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/samplers/__init__.py +0 -0
  164. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/samplers/ssl.py +0 -0
  165. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/training/__init__.py +0 -0
  166. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/training/callbacks.py +0 -0
  167. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/training/scoring.py +0 -0
  168. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/util.py +0 -0
  169. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/visualization/__init__.py +0 -0
  170. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/visualization/activations.py +0 -0
  171. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/visualization/attribution.py +0 -0
  172. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/visualization/confusion_matrices.py +0 -0
  173. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/visualization/frequency.py +0 -0
  174. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/visualization/metrics.py +0 -0
  175. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/visualization/sanity.py +0 -0
  176. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode/visualization/topology.py +0 -0
  177. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode.egg-info/dependency_links.txt +0 -0
  178. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode.egg-info/requires.txt +0 -0
  179. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/braindecode.egg-info/top_level.txt +0 -0
  180. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/Makefile +0 -0
  181. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/_templates/autosummary/class.rst +0 -0
  182. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
  183. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/_templates/autosummary/function.rst +0 -0
  184. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
  185. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/agent_docs.rst +0 -0
  186. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/cite.rst +0 -0
  187. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/conf.py +0 -0
  188. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/help.rst +0 -0
  189. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/index.rst +0 -0
  190. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/install/install.rst +0 -0
  191. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/install/install_pip.rst +0 -0
  192. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/install/install_source.rst +0 -0
  193. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/attention.rst +0 -0
  194. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/channel.rst +0 -0
  195. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/convolution.rst +0 -0
  196. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/filterbank.rst +0 -0
  197. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/gnn.rst +0 -0
  198. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/interpretable.rst +0 -0
  199. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/lbm.rst +0 -0
  200. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/recurrent.rst +0 -0
  201. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/categorization/spd.rst +0 -0
  202. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/models.rst +0 -0
  203. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/models_categorization.rst +0 -0
  204. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/models_table.rst +0 -0
  205. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/models/models_visualization.rst +0 -0
  206. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/sg_execution_times.rst +0 -0
  207. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/docs/user_guide/channel_strategies.rst +0 -0
  208. {braindecode-1.8.1.dev183280391 → braindecode-1.8.1.dev183301950}/pyproject.toml +0 -0
  209. {braindecode-1.8.1.dev183280391 → 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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: braindecode
3
- Version: 1.8.1.dev183280391
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>
@@ -1518,7 +1518,9 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
1518
1518
  metadata : pd.DataFrame
1519
1519
  DataFrame containing as many rows as there are windows in the
1520
1520
  BaseConcatDataset, with the metadata and description information
1521
- 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.
1522
1524
  """
1523
1525
  if not all(
1524
1526
  [
@@ -1544,9 +1546,15 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
1544
1546
  "Dataset descriptions cannot contain the reserved window "
1545
1547
  "metadata key 'i_trial_in_dataset'."
1546
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
+ )
1547
1555
 
1548
1556
  all_dfs = list()
1549
- for ds in self.datasets:
1557
+ for i_ds, ds in enumerate(self.datasets):
1550
1558
  if hasattr(ds, "_windows") and ds._windows is not None:
1551
1559
  df = ds._windows.metadata
1552
1560
  else:
@@ -1556,6 +1564,7 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
1556
1564
  df = df.copy()
1557
1565
  for k, v in ds.description.items():
1558
1566
  df[k] = v
1567
+ df["i_dataset"] = i_ds
1559
1568
  all_dfs.append(df)
1560
1569
 
1561
1570
  return pd.concat(all_dfs)
@@ -91,16 +91,27 @@ def fft_conv1d(
91
91
  n_fft = 2 * _next_fast_len((n_times + kernel_size) // 2)
92
92
  xs = spectral_input(x)
93
93
  # conv1d is a cross-correlation: a convolution with the flipped kernel
94
- x_f = torch.fft.rfft(xs, n=n_fft).permute(2, 0, 1) # (freq, batch, in)
94
+ x_f = torch.fft.rfft(xs, n=n_fft) # (batch, in, freq)
95
95
  w_f = torch.fft.rfft(weight.to(xs).flip(-1), n=n_fft).permute(2, 1, 0)
96
96
  # Per frequency (batch, in) @ (in, out) in complex numbers, as one real
97
- # matmul (a complex one is slower on CPU): [Re x; Im x] @ [Re w, Im w]
98
- # (columns interleaved) holds the four real products.
99
- p = torch.cat([x_f.real, x_f.imag], 1) @ torch.view_as_real(w_f).flatten(2)
100
- p = p.unflatten(1, [2, x.shape[0]]).unflatten(3, [-1, 2]) # x re/im, w re/im
101
- y_f = torch.complex(
102
- p[:, 0, :, :, 0] - p[:, 1, :, :, 1], p[:, 0, :, :, 1] + p[:, 1, :, :, 0]
103
- )
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
+ )
104
115
  y = torch.fft.irfft(y_f.permute(1, 2, 0), n=n_fft)
105
116
  # "same" output t is sample t + kernel_size // 2 of the full convolution
106
117
  y = y[..., kernel_size // 2 : kernel_size // 2 + n_times]
@@ -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",
@@ -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 = F.scaled_dot_product_attention(
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
- restricted = self.temporal_out(self._attend(per_channel, band)).reshape(
480
- batch, n_chans, n_patches, dim
481
- )
482
- full = restricted
483
- if band is not None or (self.training and self.att_drop_prob > 0):
484
- full = self.temporal_out(self._attend(per_channel, None)).reshape(
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
- temporal = scale_weights[..., 0:1] * restricted + scale_weights[..., 1:2] * full
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(
@@ -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
- nn.GLU(dim=1),
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]
@@ -17,6 +17,7 @@ from torch.nn.utils.parametrizations import weight_norm
17
17
  from braindecode.functional import spectral_input
18
18
  from braindecode.functional.functions import _next_fast_len
19
19
  from braindecode.models.base import EEGModuleMixin
20
+ from braindecode.modules.attention import _hpu_lazy
20
21
 
21
22
 
22
23
  class CodeBrain(EEGModuleMixin, nn.Module):
@@ -831,10 +832,20 @@ class _ResidualGroup(nn.Module):
831
832
  def forward(self, input_data: torch.Tensor) -> torch.Tensor:
832
833
  noise = input_data
833
834
  h = noise
834
- skip = torch.zeros((), dtype=h.dtype, device=h.device)
835
- for block in self.residual_blocks:
836
- h, skip_n = block((h, noise))
837
- skip = skip_n + skip
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
838
849
 
839
850
  return skip * math.sqrt(1.0 / self.num_res_layers)
840
851
 
@@ -378,7 +378,15 @@ class _PatchEmbeddingEEGNet(nn.Module):
378
378
  Tensor
379
379
  Embedded patches of shape (batch_size, num_patches, embedding_dim).
380
380
  """
381
- x = self.eegnet_module(x)
381
+ layers = self.eegnet_module
382
+ x = layers[1](layers[0](x))
383
+ # The depthwise (n_chans, 1) convolution as a matmul over the channel
384
+ # axis: same values, a faster backward than the grouped convolution's.
385
+ b, f, c, t = x.shape
386
+ x = torch.matmul(layers[2].weight.view(f, -1, c), x).view(b, -1, 1, t)
387
+ for i, layer in enumerate(layers):
388
+ if i > 2:
389
+ x = layer(x)
382
390
  x = self.projection(x)
383
391
  return x
384
392
 
@@ -4,6 +4,7 @@
4
4
  import warnings
5
5
 
6
6
  import torch
7
+ import torch.nn.functional as F
7
8
  from einops.layers.torch import Rearrange
8
9
  from torch import Tensor, nn
9
10
 
@@ -346,7 +347,19 @@ class _PatchEmbedding(nn.Module):
346
347
  )
347
348
 
348
349
  def forward(self, x: Tensor) -> Tensor:
349
- x = self.shallownet(x)
350
+ # Nothing runs between the temporal and the spatial convolution, so
351
+ # they run as one convolution with the composed weight and bias (as
352
+ # CombinedConv does), without the (batch, filters, chans, time)
353
+ # intermediate.
354
+ conv_time, conv_spat = self.shallownet[0], self.shallownet[1]
355
+ bias_time, bias_spat = conv_time.bias, conv_spat.bias
356
+ assert bias_time is not None and bias_spat is not None
357
+ weight = (conv_time.weight * conv_spat.weight.permute(1, 0, 2, 3)).sum(0)
358
+ bias = conv_spat.weight.sum((2, 3)) @ bias_time + bias_spat
359
+ x = F.conv1d(x.squeeze(1), weight, bias).unsqueeze(2)
360
+ for i, layer in enumerate(self.shallownet):
361
+ if i > 1:
362
+ x = layer(x)
350
363
  x = self.projection(x)
351
364
  return x
352
365
 
@@ -358,10 +358,14 @@ class EEGNeX(EEGModuleMixin, nn.Module):
358
358
  class _TemporalConv(nn.Conv2d):
359
359
  """``(1, k)`` Conv2d with "same" output length; on CPU and HPU, through
360
360
  :func:`~braindecode.functional.fft_conv1d` where
361
- :func:`~braindecode.functional.prefer_fft_conv` expects it to be faster."""
361
+ :func:`~braindecode.functional.prefer_fft_conv` expects it to be faster.
362
+ On HPU only when gradients are computed: the FFT pays off in the
363
+ backward, and Gaudi's direct forward alone is cheaper."""
362
364
 
363
365
  def forward(self, x: torch.Tensor) -> torch.Tensor:
364
- if not prefer_fft_conv(x, self.kernel_size[1]):
366
+ if not prefer_fft_conv(x, self.kernel_size[1]) or (
367
+ x.device.type == "hpu" and not torch.is_grad_enabled()
368
+ ):
365
369
  return self._conv_forward(x, self.weight, self.bias)
366
370
  b, _, h, t = x.shape # the conv1d of each of the b * h rows
367
371
  x = x.transpose(1, 2).reshape(b * h, -1, t)
@@ -13,6 +13,7 @@ from torch import nn
13
13
 
14
14
  from braindecode.models.base import EEGModuleMixin
15
15
  from braindecode.modules import DropPath
16
+ from braindecode.modules.attention import _scaled_dot_product_attention
16
17
  from braindecode.modules.convolution import Conv1dWithConstraint
17
18
  from braindecode.modules.linear import LinearWithConstraint
18
19
  from braindecode.util import resolve_montage_name
@@ -949,14 +950,17 @@ class _Attention(nn.Module):
949
950
  # 3. Flash Attention
950
951
  # Use PyTorch's optimized scaled_dot_product_attention (SDPA)
951
952
  # which internally uses FlashAttention or EfficientAttention kernels.
952
- y = torch.nn.functional.scaled_dot_product_attention(
953
- q,
954
- k,
955
- v,
956
- attn_mask=None,
957
- dropout_p=self.attn_drop.p if self.training else 0.0,
958
- is_causal=self.is_causal,
959
- )
953
+ dropout_p = self.attn_drop.p if self.training else 0.0
954
+ if self.is_causal:
955
+ y = torch.nn.functional.scaled_dot_product_attention(
956
+ q, k, v, attn_mask=None, dropout_p=dropout_p, is_causal=True
957
+ )
958
+ else:
959
+ # Gaudi's fused kernel also in lazy mode: faster for EEGPT's
960
+ # 23-token sequences except on the shortest inputs (256 samples).
961
+ y = _scaled_dot_product_attention(
962
+ q, k, v, dropout_p=dropout_p, fused_in_lazy=True
963
+ )
960
964
 
961
965
  # Reshape back: (batch, num_heads, seq_len, head_dim) -> (batch, seq_len, embed_dim)
962
966
  x = y.transpose(1, 2).flatten(2)
@@ -457,6 +457,58 @@ class _SpatialConv3d(nn.Conv3d):
457
457
  return y.reshape(b, -1, z, t, y.shape[-1])
458
458
 
459
459
 
460
+ class _BatchNorm3d(nn.BatchNorm3d):
461
+ """``nn.BatchNorm3d`` with running statistics (as built here); on HPU it
462
+ normalises the ``(batch, channels, -1)`` view, same statistics and
463
+ buffers: Gaudi's 5-D batch norm backward is 2-7x slower than its 3-D one
464
+ on these activations. Elsewhere the same ``batch_norm`` call as torch's."""
465
+
466
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
467
+ if self.training and self.num_batches_tracked is not None:
468
+ self.num_batches_tracked.add_(1)
469
+ y = nn.functional.batch_norm(
470
+ x.flatten(2) if x.device.type == "hpu" else x,
471
+ self.running_mean,
472
+ self.running_var,
473
+ self.weight,
474
+ self.bias,
475
+ self.training,
476
+ self.momentum, # type: ignore[arg-type]
477
+ self.eps,
478
+ )
479
+ return y.view_as(x)
480
+
481
+
482
+ class _TemporalConv3d(nn.Conv3d):
483
+ """``nn.Conv3d`` with a ``(1, k, 1)`` kernel (padding on time only); on HPU
484
+ it runs as ``conv1d`` on a ``(batch * z * space, channels, time)`` copy of
485
+ the input, same parameters. Gaudi's conv3d along time, which is not the
486
+ innermost axis here, is 10-50x slower than that conv1d."""
487
+
488
+ def __init__(self, *args, **kwargs):
489
+ super().__init__(*args, **kwargs)
490
+ self.time_padding = (
491
+ self.padding if isinstance(self.padding, str) else self.padding[1]
492
+ )
493
+
494
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
495
+ if x.device.type == "hpu":
496
+ return self._time_last(x)
497
+ return self._conv_forward(x, self.weight, self.bias)
498
+
499
+ def _time_last(self, x: torch.Tensor) -> torch.Tensor:
500
+ b, c, z, t, s = x.shape
501
+ y = nn.functional.conv1d(
502
+ x.permute(0, 2, 4, 1, 3).reshape(b * z * s, c, t),
503
+ self.weight.flatten(2),
504
+ self.bias,
505
+ padding=self.time_padding,
506
+ groups=self.groups,
507
+ )
508
+ y = y.reshape(b, z, s, y.shape[1], y.shape[2])
509
+ return y.permute(0, 3, 1, 4, 2).contiguous()
510
+
511
+
460
512
  class _InceptionBlock(nn.Module):
461
513
  r"""Inception module used in EEGSym architecture.
462
514
 
@@ -498,13 +550,13 @@ class _InceptionBlock(nn.Module):
498
550
  for scale in scales_samples:
499
551
  self.temporal_convs.append(
500
552
  nn.Sequential(
501
- nn.Conv3d(
553
+ _TemporalConv3d(
502
554
  in_channels=in_channels,
503
555
  out_channels=filters_per_branch,
504
556
  kernel_size=(1, scale, 1),
505
557
  padding="same",
506
558
  ),
507
- nn.BatchNorm3d(filters_per_branch, eps=1e-3, momentum=0.01),
559
+ _BatchNorm3d(filters_per_branch, eps=1e-3, momentum=0.01),
508
560
  activation,
509
561
  nn.Dropout(drop_prob),
510
562
  )
@@ -527,7 +579,7 @@ class _InceptionBlock(nn.Module):
527
579
  groups=filters_per_branch * len(scales_samples),
528
580
  bias=False,
529
581
  ),
530
- nn.BatchNorm3d(
582
+ _BatchNorm3d(
531
583
  filters_per_branch * len(scales_samples),
532
584
  eps=1e-3,
533
585
  momentum=0.01,
@@ -598,13 +650,13 @@ class _ResidualBlock(nn.Module):
598
650
 
599
651
  # Temporal convolution
600
652
  self.temporal_conv = nn.Sequential(
601
- nn.Conv3d(
653
+ _TemporalConv3d(
602
654
  in_channels=in_channels,
603
655
  out_channels=filters,
604
656
  kernel_size=(1, kernel_size, 1),
605
657
  padding="same",
606
658
  ),
607
- nn.BatchNorm3d(filters, eps=1e-3, momentum=0.01),
659
+ _BatchNorm3d(filters, eps=1e-3, momentum=0.01),
608
660
  activation,
609
661
  nn.Dropout(drop_prob),
610
662
  )
@@ -618,7 +670,7 @@ class _ResidualBlock(nn.Module):
618
670
  kernel_size=(1, 1, 1),
619
671
  bias=False,
620
672
  ),
621
- nn.BatchNorm3d(filters, eps=1e-3, momentum=0.01),
673
+ _BatchNorm3d(filters, eps=1e-3, momentum=0.01),
622
674
  activation,
623
675
  nn.Dropout(drop_prob),
624
676
  )
@@ -642,7 +694,7 @@ class _ResidualBlock(nn.Module):
642
694
  groups=1,
643
695
  bias=False,
644
696
  ),
645
- nn.BatchNorm3d(filters, eps=1e-3, momentum=0.01),
697
+ _BatchNorm3d(filters, eps=1e-3, momentum=0.01),
646
698
  activation,
647
699
  nn.Dropout(drop_prob),
648
700
  )
@@ -694,14 +746,14 @@ class _TemporalBlock(nn.Module):
694
746
  super().__init__()
695
747
 
696
748
  self.conv = nn.Sequential(
697
- nn.Conv3d(
749
+ _TemporalConv3d(
698
750
  in_channels=in_channels,
699
751
  out_channels=filters,
700
752
  kernel_size=(1, kernel_size, 1),
701
753
  padding="same",
702
754
  bias=False,
703
755
  ),
704
- nn.BatchNorm3d(filters, eps=1e-3, momentum=0.01),
756
+ _BatchNorm3d(filters, eps=1e-3, momentum=0.01),
705
757
  activation,
706
758
  nn.Dropout(drop_prob),
707
759
  )
@@ -762,7 +814,7 @@ class _ChannelMergingBlock(nn.Module):
762
814
  padding=(0, 0, 0), # Valid padding
763
815
  bias=False,
764
816
  ),
765
- nn.BatchNorm3d(filters, eps=1e-3, momentum=0.01),
817
+ _BatchNorm3d(filters, eps=1e-3, momentum=0.01),
766
818
  activation,
767
819
  nn.Dropout(drop_prob),
768
820
  )
@@ -779,7 +831,7 @@ class _ChannelMergingBlock(nn.Module):
779
831
  padding=(0, 0, 0),
780
832
  bias=False,
781
833
  ),
782
- nn.BatchNorm3d(filters, eps=1e-3, momentum=0.01),
834
+ _BatchNorm3d(filters, eps=1e-3, momentum=0.01),
783
835
  activation,
784
836
  nn.Dropout(drop_prob),
785
837
  )
@@ -837,21 +889,21 @@ class _TemporalMergingBlock(nn.Module):
837
889
 
838
890
  # Residual convolution (collapses time dimension)
839
891
  self.residual_conv = nn.Sequential(
840
- nn.Conv3d(
892
+ _TemporalConv3d(
841
893
  in_channels=in_channels,
842
894
  out_channels=in_channels, # Same channels for residual
843
895
  kernel_size=(1, self.temporal_kernel, 1), # (Z, Time, Space)
844
896
  padding=(0, 0, 0), # Valid padding - reduces time to 1
845
897
  bias=False,
846
898
  ),
847
- nn.BatchNorm3d(in_channels, eps=1e-3, momentum=0.01),
899
+ _BatchNorm3d(in_channels, eps=1e-3, momentum=0.01),
848
900
  activation,
849
901
  nn.Dropout(drop_prob),
850
902
  )
851
903
 
852
904
  # Grouped convolution (collapses time dimension, doubles filters)
853
905
  self.grouped_conv = nn.Sequential(
854
- nn.Conv3d(
906
+ _TemporalConv3d(
855
907
  in_channels=in_channels,
856
908
  out_channels=filters, # Double the channels
857
909
  kernel_size=(1, self.temporal_kernel, 1), # (Z, Time, Space)
@@ -859,7 +911,7 @@ class _TemporalMergingBlock(nn.Module):
859
911
  padding=(0, 0, 0),
860
912
  bias=False,
861
913
  ),
862
- nn.BatchNorm3d(filters, eps=1e-3, momentum=0.01),
914
+ _BatchNorm3d(filters, eps=1e-3, momentum=0.01),
863
915
  activation,
864
916
  nn.Dropout(drop_prob),
865
917
  )
@@ -908,7 +960,7 @@ class _OutputBlock(nn.Module):
908
960
  padding=(0, 0, 0),
909
961
  bias=False,
910
962
  ),
911
- nn.BatchNorm3d(in_channels, eps=1e-3, momentum=0.01),
963
+ _BatchNorm3d(in_channels, eps=1e-3, momentum=0.01),
912
964
  activation,
913
965
  nn.Dropout(drop_prob),
914
966
  )