braindecode 1.8.1.dev173387713__tar.gz → 1.8.1.dev174857482__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 (194) hide show
  1. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/NOTICE.txt +2 -0
  2. {braindecode-1.8.1.dev173387713/braindecode.egg-info → braindecode-1.8.1.dev174857482}/PKG-INFO +3 -1
  3. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/__init__.py +2 -0
  4. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/deep4.py +16 -8
  5. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegpt.py +14 -2
  6. braindecode-1.8.1.dev174857482/braindecode/models/popt.py +320 -0
  7. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/shallow_fbcsp.py +15 -8
  8. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/summary.csv +1 -0
  9. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/util.py +1 -0
  10. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/convolution.py +25 -14
  11. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/layers.py +4 -2
  12. braindecode-1.8.1.dev174857482/braindecode/modules/popt_modules.py +187 -0
  13. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/training/losses.py +7 -2
  14. braindecode-1.8.1.dev174857482/braindecode/version.py +1 -0
  15. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482/braindecode.egg-info}/PKG-INFO +3 -1
  16. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode.egg-info/SOURCES.txt +3 -0
  17. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode.egg-info/requires.txt +2 -0
  18. braindecode-1.8.1.dev174857482/docs/agent_docs.rst +60 -0
  19. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/api.rst +1 -0
  20. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/conf.py +7 -0
  21. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/whats_new.rst +46 -0
  22. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/pyproject.toml +2 -0
  23. braindecode-1.8.1.dev173387713/braindecode/version.py +0 -1
  24. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/LICENSE.txt +0 -0
  25. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/MANIFEST.in +0 -0
  26. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/README.rst +0 -0
  27. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/__init__.py +0 -0
  28. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/augmentation/__init__.py +0 -0
  29. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/augmentation/base.py +0 -0
  30. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/augmentation/functional.py +0 -0
  31. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/augmentation/transforms.py +0 -0
  32. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/classifier.py +0 -0
  33. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/__init__.py +0 -0
  34. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/_notebook_viewer.py +0 -0
  35. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/base.py +0 -0
  36. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bbci.py +0 -0
  37. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bcicomp.py +0 -0
  38. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/__init__.py +0 -0
  39. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/datasets.py +0 -0
  40. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/format.py +0 -0
  41. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/hub.py +0 -0
  42. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/hub_format.py +0 -0
  43. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/hub_io.py +0 -0
  44. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/hub_validation.py +0 -0
  45. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/iterable.py +0 -0
  46. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/chb_mit.py +0 -0
  47. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/collate.py +0 -0
  48. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/mne.py +0 -0
  49. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/moabb.py +0 -0
  50. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/nmt.py +0 -0
  51. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/registry.py +0 -0
  52. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/siena.py +0 -0
  53. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
  54. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/sleep_physionet.py +0 -0
  55. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/tuh.py +0 -0
  56. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/utils.py +0 -0
  57. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/xy.py +0 -0
  58. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/__init__.py +0 -0
  59. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/channel_utils.py +0 -0
  60. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/hub_formats.py +0 -0
  61. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/serialization.py +0 -0
  62. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/util.py +0 -0
  63. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/eegneuralnet.py +0 -0
  64. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/functional/__init__.py +0 -0
  65. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/functional/_real_dft.py +0 -0
  66. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/functional/functions.py +0 -0
  67. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/functional/initialization.py +0 -0
  68. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/atcnet.py +0 -0
  69. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/attentionbasenet.py +0 -0
  70. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/attn_sleep.py +0 -0
  71. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/barista.py +0 -0
  72. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/base.py +0 -0
  73. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/bendr.py +0 -0
  74. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/biot.py +0 -0
  75. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/brainbert.py +0 -0
  76. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/brainmodule.py +0 -0
  77. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/brant.py +0 -0
  78. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/cbramod.py +0 -0
  79. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/codebrain.py +0 -0
  80. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/config.py +0 -0
  81. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/contrawr.py +0 -0
  82. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/ctnet.py +0 -0
  83. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/dance.py +0 -0
  84. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/deepsleepnet.py +0 -0
  85. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/dgcnn.py +0 -0
  86. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/diver1.py +0 -0
  87. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegconformer.py +0 -0
  88. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegdino.py +0 -0
  89. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eeginception_erp.py +0 -0
  90. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eeginception_mi.py +0 -0
  91. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegitnet.py +0 -0
  92. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegminer.py +0 -0
  93. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegnet.py +0 -0
  94. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegnex.py +0 -0
  95. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegsimpleconv.py +0 -0
  96. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegsym.py +0 -0
  97. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegtcnet.py +0 -0
  98. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/emg2qwerty.py +0 -0
  99. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/fbcnet.py +0 -0
  100. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/fblightconvnet.py +0 -0
  101. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/fbmsnet.py +0 -0
  102. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/hybrid.py +0 -0
  103. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/ifnet.py +0 -0
  104. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/interpolated.py +0 -0
  105. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/labram.py +0 -0
  106. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/luna.py +0 -0
  107. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/medformer.py +0 -0
  108. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/meta_neuromotor.py +0 -0
  109. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/mirepnet.py +0 -0
  110. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/mscformer.py +0 -0
  111. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/msvtnet.py +0 -0
  112. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/mvpformer.py +0 -0
  113. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/neuropose.py +0 -0
  114. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/patchedtransformer.py +0 -0
  115. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/reve.py +0 -0
  116. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sccnet.py +0 -0
  117. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sensingdynamics.py +0 -0
  118. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/signal_jepa.py +0 -0
  119. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sinc_shallow.py +0 -0
  120. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
  121. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
  122. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sparcnet.py +0 -0
  123. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sstdpn.py +0 -0
  124. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/steegformer.py +0 -0
  125. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/syncnet.py +0 -0
  126. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/tcformer.py +0 -0
  127. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/tcn.py +0 -0
  128. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/tidnet.py +0 -0
  129. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/tsinception.py +0 -0
  130. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/usleep.py +0 -0
  131. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/vemg2pose.py +0 -0
  132. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/zuna.py +0 -0
  133. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/__init__.py +0 -0
  134. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/activation.py +0 -0
  135. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/attention.py +0 -0
  136. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/blocks.py +0 -0
  137. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/dance_modules.py +0 -0
  138. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/filter.py +0 -0
  139. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/interpolation.py +0 -0
  140. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/linear.py +0 -0
  141. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/parametrization.py +0 -0
  142. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/stats.py +0 -0
  143. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/util.py +0 -0
  144. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/wrapper.py +0 -0
  145. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/__init__.py +0 -0
  146. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
  147. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/mne_preprocess.py +0 -0
  148. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/preprocess.py +0 -0
  149. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/util.py +0 -0
  150. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/windowers.py +0 -0
  151. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/regressor.py +0 -0
  152. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/samplers/__init__.py +0 -0
  153. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/samplers/base.py +0 -0
  154. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/samplers/ssl.py +0 -0
  155. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/training/__init__.py +0 -0
  156. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/training/callbacks.py +0 -0
  157. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/training/scoring.py +0 -0
  158. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/util.py +0 -0
  159. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/__init__.py +0 -0
  160. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/activations.py +0 -0
  161. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/attribution.py +0 -0
  162. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/confusion_matrices.py +0 -0
  163. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/frequency.py +0 -0
  164. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/metrics.py +0 -0
  165. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/sanity.py +0 -0
  166. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/topology.py +0 -0
  167. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode.egg-info/dependency_links.txt +0 -0
  168. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode.egg-info/top_level.txt +0 -0
  169. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/Makefile +0 -0
  170. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/_templates/autosummary/class.rst +0 -0
  171. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
  172. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/_templates/autosummary/function.rst +0 -0
  173. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
  174. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/cite.rst +0 -0
  175. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/help.rst +0 -0
  176. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/index.rst +0 -0
  177. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/install/install.rst +0 -0
  178. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/install/install_pip.rst +0 -0
  179. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/install/install_source.rst +0 -0
  180. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/attention.rst +0 -0
  181. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/channel.rst +0 -0
  182. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/convolution.rst +0 -0
  183. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/filterbank.rst +0 -0
  184. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/gnn.rst +0 -0
  185. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/interpretable.rst +0 -0
  186. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/lbm.rst +0 -0
  187. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/recurrent.rst +0 -0
  188. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/spd.rst +0 -0
  189. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/models.rst +0 -0
  190. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/models_categorization.rst +0 -0
  191. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/models_table.rst +0 -0
  192. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/models_visualization.rst +0 -0
  193. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/sg_execution_times.rst +0 -0
  194. {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/setup.cfg +0 -0
@@ -43,6 +43,8 @@ The following components are licensed under the MIT License:
43
43
  - `braindecode/models/tcformer.py`
44
44
  - `braindecode/models/ifnet.py`
45
45
  - `braindecode/models/mirepnet.py`
46
+ - `braindecode/models/popt.py`
47
+ - `braindecode/modules/popt_modules.py`
46
48
 
47
49
  ### Apache-2.0 Licensed Files
48
50
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: braindecode
3
- Version: 1.8.1.dev173387713
3
+ Version: 1.8.1.dev174857482
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>
@@ -60,6 +60,8 @@ Provides-Extra: typing
60
60
  Requires-Dist: exca>=0.5.10; extra == "typing"
61
61
  Requires-Dist: numpydantic>=1.7; extra == "typing"
62
62
  Provides-Extra: docs
63
+ Requires-Dist: pytest<9.1; extra == "docs"
64
+ Requires-Dist: sphinx-markdown-builder==0.6.11; extra == "docs"
63
65
  Requires-Dist: sphinx_gallery>=0.17; extra == "docs"
64
66
  Requires-Dist: sphinx_rtd_theme; extra == "docs"
65
67
  Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
@@ -48,6 +48,7 @@ from .msvtnet import MSVTNet
48
48
  from .mvpformer import MVPFormer
49
49
  from .neuropose import NeuroPose
50
50
  from .patchedtransformer import PBT
51
+ from .popt import PopulationTransformer
51
52
  from .reve import REVE
52
53
  from .sccnet import SCCNet
53
54
  from .sensingdynamics import SensingDynamics
@@ -144,6 +145,7 @@ __all__ = [
144
145
  "MSVTNet",
145
146
  "MVPFormer",
146
147
  "PBT",
148
+ "PopulationTransformer",
147
149
  "REVE",
148
150
  "SCCNet",
149
151
  "ShallowFBCSPNet",
@@ -186,13 +186,18 @@ class Deep4Net(EEGModuleMixin, nn.Sequential):
186
186
  # When padronize all layers,
187
187
  # add the old's parameters here
188
188
  self.mapping = {
189
- "conv_time.weight": "conv_time_spat.conv_time.weight",
190
- "conv_spat.weight": "conv_time_spat.conv_spat.weight",
191
- "conv_time.bias": "conv_time_spat.conv_time.bias",
192
- "conv_spat.bias": "conv_time_spat.conv_spat.bias",
193
189
  "conv_classifier.weight": "final_layer.conv_classifier.weight",
194
190
  "conv_classifier.bias": "final_layer.conv_classifier.bias",
195
191
  }
192
+ if self.split_first_layer:
193
+ self.mapping.update(
194
+ {
195
+ "conv_time.weight": "conv_time_spat.conv_time.weight",
196
+ "conv_spat.weight": "conv_time_spat.conv_spat.weight",
197
+ "conv_time.bias": "conv_time_spat.conv_time.bias",
198
+ "conv_spat.bias": "conv_time_spat.conv_spat.bias",
199
+ }
200
+ )
196
201
 
197
202
  if self.stride_before_pool:
198
203
  conv_stride = self.pool_time_stride
@@ -215,6 +220,7 @@ class Deep4Net(EEGModuleMixin, nn.Sequential):
215
220
  filter_time_length=filter_time_length,
216
221
  bias_time=True,
217
222
  bias_spat=not self.batch_norm,
223
+ stride=(conv_stride, 1),
218
224
  ),
219
225
  )
220
226
  n_filters_conv = self.n_filters_spat
@@ -318,14 +324,16 @@ class Deep4Net(EEGModuleMixin, nn.Sequential):
318
324
 
319
325
  # Initialization, xavier is same as in our paper...
320
326
  # was default from lasagne
321
- init.xavier_uniform_(self.conv_time_spat.conv_time.weight, gain=1)
322
- # maybe no bias in case of no split layer and batch norm
323
- if self.split_first_layer or (not self.batch_norm):
324
- init.constant_(self.conv_time_spat.conv_time.bias, 0)
325
327
  if self.split_first_layer:
328
+ init.xavier_uniform_(self.conv_time_spat.conv_time.weight, gain=1)
329
+ init.constant_(self.conv_time_spat.conv_time.bias, 0)
326
330
  init.xavier_uniform_(self.conv_time_spat.conv_spat.weight, gain=1)
327
331
  if not self.batch_norm:
328
332
  init.constant_(self.conv_time_spat.conv_spat.bias, 0)
333
+ else:
334
+ init.xavier_uniform_(self.conv_time.weight, gain=1)
335
+ if not self.batch_norm:
336
+ init.constant_(self.conv_time.bias, 0)
329
337
  if self.batch_norm:
330
338
  init.constant_(self.bnorm.weight, 1)
331
339
  init.constant_(self.bnorm.bias, 0)
@@ -88,7 +88,8 @@ class EEGPT(EEGModuleMixin, nn.Module):
88
88
  - *Role.* Maps raw spatio-temporal EEG patches into a sequence of latent tokens :math:`z`.
89
89
  - `EEGPT.chans_id` **(Channel Identification)**
90
90
  - *Operations.* A buffer containing channel indices mapped from the standard channel names provided
91
- in ``chs_info`` [eegpt]_.
91
+ in ``chs_info`` [eegpt]_. It is rebuilt from the montage at construction and not stored in
92
+ checkpoints, so pretrained weights load on any montage.
92
93
  - *Role.* Provides the spatial identity for each input channel, allowing the model to look up
93
94
  the correct channel embedding vector :math:`\varsigma_i`.
94
95
  - **Local Spatio-Temporal Embedding** (Input Processing)
@@ -338,8 +339,12 @@ class EEGPT(EEGModuleMixin, nn.Module):
338
339
  else:
339
340
  self.channel_names = None # type: ignore
340
341
 
342
+ # Derived from the montage above, so it is not saved in the state dict:
343
+ # loading a checkpoint must not replace the IDs of this model's channels.
341
344
  self.register_buffer(
342
- "chans_id", self.target_encoder.prepare_chan_ids(self.channel_names)
345
+ "chans_id",
346
+ self.target_encoder.prepare_chan_ids(self.channel_names),
347
+ persistent=False,
343
348
  )
344
349
 
345
350
  self.flattened_encoder_output_dim = (
@@ -391,6 +396,13 @@ class EEGPT(EEGModuleMixin, nn.Module):
391
396
  "embed_dim": self.embed_dim,
392
397
  }
393
398
 
399
+ def _load_from_state_dict(self, state_dict, prefix, *args, **kwargs):
400
+ # Checkpoints saved before ``chans_id`` became non-persistent (including
401
+ # the released EEGPT weights) still carry it. Drop it, so the IDs keep
402
+ # matching this model's montage and a different montage can load.
403
+ state_dict.pop(prefix + "chans_id", None)
404
+ super()._load_from_state_dict(state_dict, prefix, *args, **kwargs)
405
+
394
406
  def forward(self, x, return_features=False):
395
407
  """
396
408
  Forward pass.
@@ -0,0 +1,320 @@
1
+ # Authors: Christopher Wang, Geeling Chau (original implementation)
2
+ # Adam Mounir <am91ris@gmail.com> (braindecode adaptation)
3
+ #
4
+ # License: MIT
5
+ # Adapted from https://github.com/czlwang/PopulationTransformer
6
+ """PopulationTransformer (PopT, Chau et al. 2024).
7
+
8
+ * paper: https://arxiv.org/abs/2406.03044
9
+ * code: https://github.com/czlwang/PopulationTransformer
10
+ * weights: https://huggingface.co/PopulationTransformer/popt_brainbert_stft
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import warnings
16
+ from collections import OrderedDict
17
+
18
+ import numpy as np
19
+ import torch
20
+ import torch.nn as nn
21
+
22
+ from braindecode.models.base import EEGModuleMixin
23
+ from braindecode.models.util import extract_channel_locations_from_chs_info
24
+ from braindecode.modules.popt_modules import (
25
+ _PopTInputEmbedding,
26
+ _PopTSpecPredictionHead,
27
+ )
28
+
29
+
30
+ class PopulationTransformer(EEGModuleMixin, nn.Module, license="mit"):
31
+ r"""PopulationTransformer (PopT) from Chau et al. (2024) [PopT2024]_.
32
+
33
+ :bdg-danger:`Foundation Model` :bdg-info:`Attention/Transformer`
34
+
35
+ PopT is a self-supervised **population** model for intracranial recordings
36
+ (sEEG/iEEG). It does not encode a raw time signal; instead each electrode is
37
+ represented by a feature vector — typically the frozen embedding of a
38
+ per-channel foundation model such as :class:`~braindecode.models.BrainBERT` —
39
+ and PopT aggregates across electrodes. Every electrode feature is linearly
40
+ projected and given a fixed sinusoidal **spatial** position encoding built
41
+ from its integer anatomical coordinates (one embedding per X/Y/Z axis plus a
42
+ sequence id). A ``CLS`` token is prepended, a stack of standard Transformer
43
+ encoder layers mixes the population, and the ``CLS`` output is the pooled
44
+ representation used for downstream decoding. Pre-training is by masked /
45
+ replaced-token modelling over the electrode population.
46
+
47
+ Following the braindecode convention, the per-electrode feature vector plays
48
+ the role of the ``n_times`` axis, so the model keeps the standard
49
+ ``(batch, n_chans, n_times)`` input signature: ``n_chans`` is the number of
50
+ electrodes and ``n_times`` is the upstream feature dimension (768 for
51
+ BrainBERT ``stft`` features). Electrode coordinates are read from
52
+ ``chs_info`` (their ``loc``) and discretised to absolute integer indices
53
+ inside the model, as upstream feeds them; when no positions are available
54
+ the electrodes fall back to distinct sequential indices.
55
+
56
+ The ``CLS`` output goes through a single linear layer, as in the upstream
57
+ fine-tuning model (``PtDownstreamModel.linear_out``, one logit trained with
58
+ binary cross-entropy there; ``n_outputs=1`` reproduces it).
59
+
60
+ The defaults are the released ``popt_brainbert_stft`` configuration:
61
+ ``hidden_dim=512``, ``ffn_dim=2048``, ``n_heads=8``, ``n_layers=6``, used
62
+ on ``n_times=768`` BrainBERT features (~20M parameters).
63
+
64
+ .. important::
65
+ **Pre-trained weights available.** The official checkpoint is released by
66
+ the authors and loads directly::
67
+
68
+ model = PopulationTransformer.from_pretrained(
69
+ "braindecode/popt-pretrained", n_outputs=2
70
+ )
71
+
72
+ It uses the default configuration; ``n_chans`` and ``n_outputs``
73
+ may be changed freely, as the population is pooled through the ``CLS``
74
+ token and the classification head is task-specific (the checkpoint
75
+ carries no trained fine-tuning head).
76
+
77
+ .. warning::
78
+ **Evaluate with a time-blocked split.** The paper's downstream results
79
+ use a random 80/10/10 split over word-aligned 5 s windows. Words are a
80
+ fraction of a second apart, so almost every test window overlaps a
81
+ training window, and labels that drift slowly in time leak into
82
+ training. Re-running the paper setup (7 subjects, 3 seeds) with
83
+ contiguous blocks of time, and dropping training windows that overlap
84
+ the test set, pretrained PopT goes from 0.79 to 0.51 ROC-AUC on Pitch
85
+ (chance) and from 0.89 to 0.64 on Volume. Onset (0.86 to 0.84) and
86
+ Speech (0.90 to 0.84) hold up, and pretraining still beats training
87
+ from scratch on Onset, Speech and Volume. The model and weights are
88
+ not affected; the issue is only in the evaluation. When fine-tuning,
89
+ split by blocks of time.
90
+
91
+ .. versionadded:: 1.8.2
92
+
93
+ Parameters
94
+ ----------
95
+ hidden_dim : int, optional
96
+ Transformer model width ``D``. Must be divisible by 8. Default 512, as
97
+ the released model.
98
+ ffn_dim : int, optional
99
+ Inner dimension of the Transformer feed-forward blocks. Default 2048, as
100
+ the released model.
101
+ n_layers : int, optional
102
+ Number of Transformer encoder layers. Default 6, as the released model.
103
+ n_heads : int, optional
104
+ Number of attention heads. Default 8, as the released model.
105
+ max_len : int, optional
106
+ Size of the coordinate table (largest addressable integer coordinate).
107
+ Default 5000, as upstream.
108
+ coord_units : {"m", "raw"}, optional
109
+ How ``chs_info`` positions become integer coordinates. ``"m"`` (default)
110
+ treats them as MNE metres and rounds them to millimetres. ``"raw"``
111
+ rounds the positions as they are: use it when ``x/y/z`` already hold the
112
+ Brain Treebank integer (left, inferior, posterior) coordinates, as NEMAR
113
+ nm000253 stores them. Either way the indices are absolute, not
114
+ shifted, as upstream feeds them (``pt_supervised_task_coords.py``).
115
+ They match the pretrained checkpoint only if the positions are already
116
+ in the upstream (left, inferior, posterior) space; MNE head-frame
117
+ positions (e.g. a standard montage) are **not** that space. Indices
118
+ outside ``[0, max_len - 1]`` are clamped, with a warning. You can also
119
+ pass ``coords`` to :meth:`forward` directly.
120
+ shift_coords : bool, optional
121
+ If ``True``, shift each axis so that its smallest index is 0. Default
122
+ ``False``. Upstream does not shift, and the shift changes the position
123
+ encoding, and so the output of the pretrained model; use it only for
124
+ positions with negative values when training from scratch.
125
+ activation : type[nn.Module], optional
126
+ Feed-forward activation, given as a class. Default :class:`~torch.nn.GELU`.
127
+ drop_prob : float, optional
128
+ Dropout probability. Default 0.1.
129
+
130
+ References
131
+ ----------
132
+ .. [PopT2024] Chau, G., Wang, C., Talukder, S., Subramaniam, V., Soedarmadji,
133
+ S., Yue, Y., Katz, B., & Barbu, A. (2024). Population Transformer:
134
+ Learning Population-level Representations of Neural Activity. arXiv
135
+ preprint arXiv:2406.03044.
136
+ """
137
+
138
+ def __init__(
139
+ self,
140
+ # braindecode parameters
141
+ n_outputs=None,
142
+ n_chans=None,
143
+ chs_info=None,
144
+ n_times=None,
145
+ input_window_seconds=None,
146
+ sfreq=None,
147
+ # model-specific parameters
148
+ *,
149
+ hidden_dim: int = 512,
150
+ ffn_dim: int = 2048,
151
+ n_layers: int = 6,
152
+ n_heads: int = 8,
153
+ max_len: int = 5000,
154
+ coord_units: str = "m",
155
+ shift_coords: bool = False,
156
+ activation: type[nn.Module] = nn.GELU,
157
+ drop_prob: float = 0.1,
158
+ ):
159
+ super().__init__(
160
+ n_outputs=n_outputs,
161
+ n_chans=n_chans,
162
+ chs_info=chs_info,
163
+ n_times=n_times,
164
+ input_window_seconds=input_window_seconds,
165
+ sfreq=sfreq,
166
+ )
167
+ del n_outputs, n_chans, chs_info, n_times, input_window_seconds, sfreq
168
+ if coord_units not in ("m", "raw"):
169
+ raise ValueError(f"coord_units must be 'm' or 'raw', got {coord_units!r}.")
170
+
171
+ self.input_dim = self.n_times
172
+ self.hidden_dim = hidden_dim
173
+ self.ffn_dim = ffn_dim
174
+ self.n_layers = n_layers
175
+ self.n_heads = n_heads
176
+ self.max_len = max_len
177
+ self.coord_units = coord_units
178
+ self.shift_coords = shift_coords
179
+
180
+ self.input_embedding = _PopTInputEmbedding(
181
+ input_dim=self.input_dim,
182
+ hidden_dim=hidden_dim,
183
+ max_len=max_len,
184
+ drop_prob=drop_prob,
185
+ )
186
+ encoder_layer = nn.TransformerEncoderLayer(
187
+ d_model=hidden_dim,
188
+ nhead=n_heads,
189
+ dim_feedforward=ffn_dim,
190
+ activation=activation(),
191
+ dropout=drop_prob,
192
+ batch_first=True,
193
+ )
194
+ self.transformer_encoder = nn.TransformerEncoder(
195
+ encoder_layer, num_layers=n_layers
196
+ )
197
+ # Pre-training head, unused here; kept so the checkpoint loads strictly.
198
+ self.spec_prediction_head = _PopTSpecPredictionHead(hidden_dim, self.input_dim)
199
+ self.final_layer = nn.Linear(hidden_dim, self.n_outputs)
200
+
201
+ self.register_buffer(
202
+ "electrode_coords", self._coords_from_chs_info(), persistent=False
203
+ )
204
+
205
+ def _coords_from_chs_info(self) -> torch.Tensor:
206
+ """Integer ``(n_chans, 3)`` coordinates from ``chs_info``, or sequential ones."""
207
+ chs_info = getattr(self, "_chs_info", None)
208
+ loc = extract_channel_locations_from_chs_info(
209
+ chs_info, num_channels=self.n_chans
210
+ )
211
+ # The helper does not screen NaNs or partial montages.
212
+ if loc is None or loc.shape[0] != self.n_chans or not np.isfinite(loc).all():
213
+ idx = torch.arange(self.n_chans, dtype=torch.long)
214
+ coords = idx.unsqueeze(1).repeat(1, 3)
215
+ else:
216
+ loc_t = torch.as_tensor(loc, dtype=torch.float)
217
+ if self.coord_units == "m":
218
+ loc_t = loc_t * 1000.0
219
+ coords = loc_t.round().long()
220
+ if self.shift_coords:
221
+ coords = coords - coords.min(dim=0, keepdim=True).values
222
+ if bool(((coords < 0) | (coords >= self.max_len)).any()):
223
+ hint = (
224
+ "raise `max_len`"
225
+ if self.shift_coords
226
+ else "set `shift_coords=True` to train from scratch"
227
+ )
228
+ warnings.warn(
229
+ "Some electrode coordinates from chs_info fall outside "
230
+ f"[0, {self.max_len - 1}] and are clamped. The pretrained "
231
+ "model expects the non-negative integer (left, inferior, "
232
+ "posterior) indices of the upstream data; pass `coords` to "
233
+ f"forward, or {hint}.",
234
+ UserWarning,
235
+ stacklevel=4,
236
+ )
237
+ return coords.clamp(0, self.max_len - 1)
238
+
239
+ def load_state_dict(self, state_dict, *args, **kwargs):
240
+ """Also accept the untrained ``final_layer.{norm,fc}`` head of the HF mirror."""
241
+ remapped = OrderedDict()
242
+ for key, value in state_dict.items():
243
+ if key.startswith("final_layer.norm."):
244
+ continue
245
+ if key.startswith("final_layer.fc."):
246
+ key = "final_layer." + key[len("final_layer.fc.") :]
247
+ remapped[key] = value
248
+ return super().load_state_dict(remapped, *args, **kwargs)
249
+
250
+ def reset_head(self, n_outputs: int) -> None:
251
+ """Swap the classification head for a new number of outputs."""
252
+ old = self.final_layer.weight
253
+ self._set_n_outputs(n_outputs)
254
+ self.final_layer = nn.Linear(self.hidden_dim, n_outputs).to(
255
+ device=old.device, dtype=old.dtype
256
+ )
257
+
258
+ def forward(
259
+ self,
260
+ x: torch.Tensor,
261
+ coords: torch.Tensor | None = None,
262
+ seq_id: torch.Tensor | None = None,
263
+ return_features: bool = False,
264
+ key_padding_mask: torch.Tensor | None = None,
265
+ ):
266
+ """Aggregate a population of electrode features.
267
+
268
+ Parameters
269
+ ----------
270
+ x : torch.Tensor
271
+ Per-electrode features of shape ``(batch, n_chans, n_times)``, where
272
+ ``n_times`` is the upstream feature dimension.
273
+ coords : torch.Tensor, optional
274
+ Integer coordinates of shape ``(batch, n_chans, 3)``. Defaults to the
275
+ coordinates derived from ``chs_info`` at construction, broadcast over
276
+ the batch.
277
+ seq_id : torch.Tensor, optional
278
+ Integer sequence ids of shape ``(batch, n_chans)``. Defaults to zero
279
+ (single population).
280
+ return_features : bool
281
+ If ``True``, return ``{"features": cls, "cls_token": cls}`` (the
282
+ pooled ``CLS`` representation) instead of the class logits.
283
+ key_padding_mask : torch.Tensor, optional
284
+ Boolean ``(batch, n_chans)`` mask, ``True`` for padded electrodes, so
285
+ recordings with different electrode sets can share a batch (upstream
286
+ ``src_key_padding_mask``). The ``CLS`` token is never masked.
287
+
288
+ Returns
289
+ -------
290
+ torch.Tensor or dict
291
+ Class logits of shape ``(batch, n_outputs)``, or the feature dict
292
+ when ``return_features`` is set.
293
+ """
294
+ batch_size, n_chans, _ = x.shape
295
+ if coords is None:
296
+ n_known = self.electrode_coords.shape[0]
297
+ if n_chans != n_known:
298
+ raise ValueError(
299
+ f"x has {n_chans} electrodes but the model has coordinates "
300
+ f"for {n_known}; pass `coords` of shape (batch, {n_chans}, 3)."
301
+ )
302
+ coords = self.electrode_coords.unsqueeze(0).expand(batch_size, -1, -1)
303
+ if seq_id is None:
304
+ seq_id = torch.zeros(batch_size, n_chans, dtype=torch.long, device=x.device)
305
+
306
+ h = self.input_embedding(x, coords, seq_id)
307
+ # New name (not reassigning the argument) keeps TorchScript's narrowing.
308
+ padded_mask: torch.Tensor | None = None
309
+ if key_padding_mask is not None:
310
+ cls_keep = torch.zeros(batch_size, 1, dtype=torch.bool, device=x.device)
311
+ padded_mask = torch.cat([cls_keep, key_padding_mask.to(torch.bool)], dim=1)
312
+ z = self.transformer_encoder(h, src_key_padding_mask=padded_mask)
313
+ cls_token = z[:, 0, :]
314
+ logits = self.final_layer(cls_token)
315
+ if return_features:
316
+ # Scripted forward stays monomorphic (same pattern as Brant).
317
+ if torch.jit.is_scripting():
318
+ return logits
319
+ return {"features": cls_token, "cls_token": cls_token}
320
+ return logits
@@ -124,13 +124,18 @@ class ShallowFBCSPNet(EEGModuleMixin, nn.Sequential):
124
124
  self.drop_prob = drop_prob
125
125
 
126
126
  self.mapping = {
127
- "conv_time.weight": "conv_time_spat.conv_time.weight",
128
- "conv_spat.weight": "conv_time_spat.conv_spat.weight",
129
- "conv_time.bias": "conv_time_spat.conv_time.bias",
130
- "conv_spat.bias": "conv_time_spat.conv_spat.bias",
131
127
  "conv_classifier.weight": "final_layer.conv_classifier.weight",
132
128
  "conv_classifier.bias": "final_layer.conv_classifier.bias",
133
129
  }
130
+ if self.split_first_layer:
131
+ self.mapping.update(
132
+ {
133
+ "conv_time.weight": "conv_time_spat.conv_time.weight",
134
+ "conv_spat.weight": "conv_time_spat.conv_spat.weight",
135
+ "conv_time.bias": "conv_time_spat.conv_time.bias",
136
+ "conv_spat.bias": "conv_time_spat.conv_spat.bias",
137
+ }
138
+ )
134
139
 
135
140
  self.add_module("ensuredims", Ensure4d())
136
141
  pool_class = dict(max=nn.MaxPool2d, mean=nn.AvgPool2d)[self.pool_mode]
@@ -203,14 +208,16 @@ class ShallowFBCSPNet(EEGModuleMixin, nn.Sequential):
203
208
  self.add_module("final_layer", module)
204
209
 
205
210
  # Initialization, xavier is same as in paper...
206
- init.xavier_uniform_(self.conv_time_spat.conv_time.weight, gain=1)
207
- # maybe no bias in case of no split layer and batch norm
208
- if self.split_first_layer or (not self.batch_norm):
209
- init.constant_(self.conv_time_spat.conv_time.bias, 0)
210
211
  if self.split_first_layer:
212
+ init.xavier_uniform_(self.conv_time_spat.conv_time.weight, gain=1)
213
+ init.constant_(self.conv_time_spat.conv_time.bias, 0)
211
214
  init.xavier_uniform_(self.conv_time_spat.conv_spat.weight, gain=1)
212
215
  if not self.batch_norm:
213
216
  init.constant_(self.conv_time_spat.conv_spat.bias, 0)
217
+ else:
218
+ init.xavier_uniform_(self.conv_time.weight, gain=1)
219
+ if not self.batch_norm:
220
+ init.constant_(self.conv_time.bias, 0)
214
221
  if self.batch_norm:
215
222
  init.constant_(self.bnorm.weight, 1)
216
223
  init.constant_(self.bnorm.bias, 0)
@@ -52,6 +52,7 @@ IFNet,Motor Imagery,Prediction,250,"n_chans, n_outputs, n_times, sfreq",9860,"IF
52
52
  BrainModule,Speech Decoding,Prediction,250,"n_chans, n_outputs, n_times, sfreq",6186909,"BrainModule(n_chans=64, n_outputs=29, n_times=160, sfreq=1000)","Convolution","EEG, MEG"
53
53
  Brant,Epilepsy,Prediction,250,"n_chans, n_outputs, n_times, sfreq",2902780,"Brant(n_chans=16, n_outputs=2, n_times=3000, sfreq=250)","Foundation Model,Attention/Transformer",iEEG
54
54
  PBT,General,Prediction,250,"n_chans, n_outputs, n_times",818948,"PBT(n_chans=22, n_outputs=4, n_times=1000, sfreq=250)","Foundation Model",EEG
55
+ PopulationTransformer,General,"Prediction, Embedding",2048,"n_chans, n_outputs, n_times",19967746,"PopulationTransformer(n_chans=8, n_outputs=2, n_times=768)","Foundation Model,Attention/Transformer",iEEG
55
56
  SSTDPN,Motor Imagery,Prediction,250,"n_chans, n_outputs, n_times",19502,"SSTDPN(n_chans=22, n_outputs=4, n_times=1000)","Convolution,Attention/Transformer",EEG
56
57
  BENDR,General,"Prediction, Embedding",250,"n_chans, n_times, n_outputs",157141049,"BENDR(n_chans=22, n_outputs=4, n_times=1000)","Foundation Model,Convolution",EEG
57
58
  LUNA,General,"Prediction, Embedding",128,"n_chans, n_times, sfreq, chs_info",7100731,"LUNA(n_chans=22, n_times=512, sfreq=128)","Convolution,Channel,Foundation Model",EEG
@@ -421,6 +421,7 @@ models_mandatory_parameters: list[
421
421
  ["n_chans", "n_outputs", "n_times"],
422
422
  {"n_times": 2048, "sfreq": 2048.0}, # the pretraining rate, 1 s windows
423
423
  ),
424
+ ("PopulationTransformer", ["n_chans", "n_outputs", "n_times"], None),
424
425
  (
425
426
  "InterpolatedBIOT",
426
427
  ["chs_info", "n_outputs", "sfreq", "n_times"],
@@ -48,6 +48,14 @@ class AvgPool2dWithConv(nn.Module):
48
48
  # that initializes parameters or something
49
49
  self._pool_weights = None
50
50
 
51
+ def _pool_weights_match_input(self, x, weight_shape):
52
+ return (
53
+ self._pool_weights is not None
54
+ and tuple(self._pool_weights.size()) == tuple(weight_shape)
55
+ and self._pool_weights.device == x.device
56
+ and self._pool_weights.dtype == x.dtype
57
+ )
58
+
51
59
  def forward(self, x):
52
60
  # Create weights for the convolution on demand:
53
61
  # size or type of x changed...
@@ -58,17 +66,10 @@ class AvgPool2dWithConv(nn.Module):
58
66
  self.kernel_size[0],
59
67
  self.kernel_size[1],
60
68
  )
61
- if self._pool_weights is None or (
62
- (tuple(self._pool_weights.size()) != tuple(weight_shape))
63
- or (self._pool_weights.is_cuda != x.is_cuda)
64
- or (self._pool_weights.data.type() != x.data.type())
65
- ):
69
+ if not self._pool_weights_match_input(x, weight_shape):
66
70
  n_pool = np.prod(self.kernel_size)
67
71
  weights = np_to_th(np.ones(weight_shape, dtype=np.float32) / float(n_pool))
68
- weights = weights.type_as(x)
69
- if x.is_cuda:
70
- weights = weights.cuda()
71
- self._pool_weights = weights
72
+ self._pool_weights = weights.to(device=x.device, dtype=x.dtype)
72
73
 
73
74
  pooled = F.conv2d(
74
75
  x,
@@ -143,6 +144,9 @@ class CombinedConv(nn.Module):
143
144
  Whether to use bias in the temporal conv
144
145
  bias_spat: bool
145
146
  Whether to use bias in the spatial conv
147
+ stride: int | tuple[int, int]
148
+ Stride of the merged convolution. This is applied at the spatial
149
+ convolution stage, matching the unmerged temporal-then-spatial form.
146
150
 
147
151
  Examples
148
152
  --------
@@ -164,6 +168,7 @@ class CombinedConv(nn.Module):
164
168
  filter_time_length=25,
165
169
  bias_time=True,
166
170
  bias_spat=True,
171
+ stride=1,
167
172
  ):
168
173
  super().__init__()
169
174
  self.bias_time = bias_time
@@ -172,7 +177,11 @@ class CombinedConv(nn.Module):
172
177
  1, n_filters_time, (filter_time_length, 1), bias=bias_time, stride=1
173
178
  )
174
179
  self.conv_spat = nn.Conv2d(
175
- n_filters_time, n_filters_spat, (1, in_chans), bias=bias_spat, stride=1
180
+ n_filters_time,
181
+ n_filters_spat,
182
+ (1, in_chans),
183
+ bias=bias_spat,
184
+ stride=stride,
176
185
  )
177
186
 
178
187
  def forward(self, x: torch.Tensor) -> torch.Tensor:
@@ -210,7 +219,9 @@ class CombinedConv(nn.Module):
210
219
 
211
220
  bias = calculated_bias
212
221
 
213
- return F.conv2d(x, weight=combined_weight, bias=bias, stride=(1, 1))
222
+ return F.conv2d(
223
+ x, weight=combined_weight, bias=bias, stride=self.conv_spat.stride
224
+ )
214
225
 
215
226
 
216
227
  class CausalConv1d(nn.Conv1d):
@@ -274,16 +285,16 @@ class CausalConv1d(nn.Conv1d):
274
285
  )
275
286
 
276
287
  def forward(self, X):
277
- out = F.conv1d(
288
+ X = F.pad(X, (self.padding[0], 0))
289
+ return F.conv1d(
278
290
  X,
279
291
  self.weight,
280
292
  self.bias,
281
293
  stride=self.stride,
282
- padding=self.padding,
294
+ padding=0,
283
295
  dilation=self.dilation,
284
296
  groups=self.groups,
285
297
  )
286
- return out[..., : -self.padding[0]]
287
298
 
288
299
 
289
300
  class DepthwiseConv2d(torch.nn.Conv2d):
@@ -62,6 +62,8 @@ class Chomp1d(nn.Module):
62
62
  return "chomp_size={}".format(self.chomp_size)
63
63
 
64
64
  def forward(self, x):
65
+ if self.chomp_size == 0:
66
+ return x.contiguous()
65
67
  return x[:, :, : -self.chomp_size].contiguous()
66
68
 
67
69
 
@@ -110,8 +112,8 @@ class TimeDistributed(nn.Module):
110
112
  Shape (batch_size, seq_len, output_size).
111
113
  """
112
114
  b, s, c, t = x.shape
113
- out = self.module(x.view(b * s, c, t))
114
- return out.view(b, s, -1)
115
+ out = self.module(x.reshape(b * s, c, t))
116
+ return out.reshape(b, s, -1)
115
117
 
116
118
 
117
119
  class DropPath(nn.Module):