braindecode 1.8.1.dev169692045__tar.gz → 1.8.1.dev169754522__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 (220) hide show
  1. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/NOTICE.txt +0 -1
  2. {braindecode-1.8.1.dev169692045/braindecode.egg-info → braindecode-1.8.1.dev169754522}/PKG-INFO +5 -4
  3. braindecode-1.8.1.dev169754522/braindecode/__init__.py +53 -0
  4. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/augmentation/base.py +99 -3
  5. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/augmentation/functional.py +4 -2
  6. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/augmentation/transforms.py +2 -1
  7. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/classifier.py +13 -2
  8. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/base.py +84 -6
  9. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bids/datasets.py +32 -8
  10. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/chb_mit.py +5 -39
  11. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/siena.py +5 -39
  12. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/sleep_physio_challe_18.py +0 -46
  13. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/utils.py +37 -0
  14. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datautil/serialization.py +15 -2
  15. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datautil/util.py +3 -2
  16. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/eegneuralnet.py +50 -8
  17. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/functional/__init__.py +31 -1
  18. braindecode-1.8.1.dev169754522/braindecode/functional/_lfilter.py +118 -0
  19. braindecode-1.8.1.dev169754522/braindecode/functional/attention.py +152 -0
  20. braindecode-1.8.1.dev169754522/braindecode/functional/channel_mask.py +214 -0
  21. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/functional/functions.py +106 -1
  22. braindecode-1.8.1.dev169754522/braindecode/functional/initialization.py +139 -0
  23. braindecode-1.8.1.dev169754522/braindecode/functional/patches.py +105 -0
  24. braindecode-1.8.1.dev169754522/braindecode/models/__init__.py +133 -0
  25. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/atcnet.py +15 -16
  26. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/attn_sleep.py +9 -13
  27. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/axon.py +11 -4
  28. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/barista.py +102 -57
  29. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/base.py +130 -14
  30. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/bendr.py +24 -18
  31. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/biot.py +12 -58
  32. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/brainbert.py +81 -41
  33. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/brainmodule.py +1 -1
  34. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/brainomni.py +23 -16
  35. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/brant.py +58 -37
  36. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/cbramod.py +12 -11
  37. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/codebrain.py +1 -1
  38. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/config.py +10 -6
  39. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/csbrain.py +14 -7
  40. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/ctnet.py +42 -177
  41. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/dance.py +6 -11
  42. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/deep4.py +6 -2
  43. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/diver1.py +154 -41
  44. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/duin.py +57 -17
  45. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegconformer.py +86 -88
  46. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegdino.py +18 -68
  47. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eeginception_erp.py +16 -5
  48. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eeginception_mi.py +7 -3
  49. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegminer.py +4 -0
  50. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegnet.py +4 -0
  51. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegpt.py +34 -354
  52. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/emg2qwerty.py +6 -2
  53. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/epint.py +66 -32
  54. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/ifnet.py +20 -24
  55. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/labram.py +8 -37
  56. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/luna.py +48 -122
  57. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/mapa.py +42 -26
  58. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/medformer.py +22 -43
  59. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/meta_neuromotor.py +6 -2
  60. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/mirepnet.py +27 -88
  61. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/mscformer.py +42 -189
  62. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/mvpformer.py +97 -21
  63. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/neuropose.py +18 -18
  64. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/neurorvq.py +47 -87
  65. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/neurorvq_tokenizer.py +37 -11
  66. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/patchedtransformer.py +37 -22
  67. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/popt.py +181 -14
  68. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/reve.py +10 -10
  69. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/sensingdynamics.py +18 -19
  70. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/shallow_fbcsp.py +6 -2
  71. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/signal_jepa.py +24 -12
  72. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/sinc_shallow.py +15 -0
  73. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/sleepfm.py +3 -3
  74. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/sparcnet.py +14 -10
  75. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/steegformer.py +18 -30
  76. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/tcformer.py +37 -37
  77. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/tidnet.py +13 -4
  78. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/tmsanet.py +20 -9
  79. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/util.py +152 -183
  80. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/vemg2pose.py +20 -22
  81. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/zuna.py +192 -30
  82. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/__init__.py +18 -0
  83. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/attention.py +102 -84
  84. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/blocks.py +42 -49
  85. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/dance_modules.py +5 -3
  86. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/filter.py +6 -8
  87. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/layers.py +234 -2
  88. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/preprocessing/eegprep_preprocess.py +61 -0
  89. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/preprocessing/mne_preprocess.py +79 -4
  90. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/preprocessing/preprocess.py +34 -24
  91. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/preprocessing/windowers.py +9 -8
  92. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/regressor.py +7 -1
  93. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/samplers/base.py +27 -17
  94. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/training/__init__.py +6 -0
  95. braindecode-1.8.1.dev169754522/braindecode/training/_trial_aggregation.py +106 -0
  96. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/training/scoring.py +335 -122
  97. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/util.py +15 -10
  98. braindecode-1.8.1.dev169754522/braindecode/version.py +1 -0
  99. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/visualization/frequency.py +11 -7
  100. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522/braindecode.egg-info}/PKG-INFO +5 -4
  101. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode.egg-info/SOURCES.txt +7 -3
  102. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode.egg-info/requires.txt +4 -3
  103. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/api.rst +28 -0
  104. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/index.rst +1 -0
  105. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/install/install.rst +1 -1
  106. braindecode-1.8.1.dev169754522/docs/user_guide/cropped_inference.rst +130 -0
  107. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/whats_new.rst +184 -2
  108. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/pyproject.toml +10 -4
  109. braindecode-1.8.1.dev169692045/braindecode/__init__.py +0 -9
  110. braindecode-1.8.1.dev169692045/braindecode/datasets/bids/format.py +0 -717
  111. braindecode-1.8.1.dev169692045/braindecode/functional/initialization.py +0 -47
  112. braindecode-1.8.1.dev169692045/braindecode/models/__init__.py +0 -197
  113. braindecode-1.8.1.dev169692045/braindecode/modules/popt_modules.py +0 -187
  114. braindecode-1.8.1.dev169692045/braindecode/version.py +0 -1
  115. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/LICENSE.txt +0 -0
  116. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/MANIFEST.in +0 -0
  117. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/README.rst +0 -0
  118. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/augmentation/__init__.py +0 -0
  119. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/__init__.py +0 -0
  120. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/_notebook_viewer.py +0 -0
  121. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bbci.py +0 -0
  122. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bcicomp.py +0 -0
  123. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bids/__init__.py +0 -0
  124. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bids/hub.py +0 -0
  125. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bids/hub_format.py +0 -0
  126. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bids/hub_io.py +0 -0
  127. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bids/hub_validation.py +0 -0
  128. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/bids/iterable.py +0 -0
  129. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/collate.py +0 -0
  130. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/mne.py +0 -0
  131. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/moabb.py +0 -0
  132. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/nmt.py +0 -0
  133. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/registry.py +0 -0
  134. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/sleep_physionet.py +0 -0
  135. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/tuh.py +0 -0
  136. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datasets/xy.py +0 -0
  137. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datautil/__init__.py +0 -0
  138. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datautil/channel_utils.py +0 -0
  139. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/datautil/hub_formats.py +0 -0
  140. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/functional/_real_dft.py +0 -0
  141. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/attentionbasenet.py +0 -0
  142. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/contrawr.py +0 -0
  143. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/deepsleepnet.py +0 -0
  144. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/dgcnn.py +0 -0
  145. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eeg_clip.py +0 -0
  146. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegitnet.py +0 -0
  147. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegnex.py +0 -0
  148. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegsimpleconv.py +0 -0
  149. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegsym.py +0 -0
  150. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/eegtcnet.py +0 -0
  151. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/fbcnet.py +0 -0
  152. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/fblightconvnet.py +0 -0
  153. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/fbmsnet.py +0 -0
  154. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/gcnsnet.py +0 -0
  155. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/guetschel2026.py +0 -0
  156. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/hybrid.py +0 -0
  157. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/msvtnet.py +0 -0
  158. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/sccnet.py +0 -0
  159. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/seizure_transformer.py +0 -0
  160. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
  161. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
  162. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/sstdpn.py +0 -0
  163. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/summary.csv +0 -0
  164. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/syncnet.py +0 -0
  165. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/tcn.py +0 -0
  166. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/tfm_tokenizer.py +0 -0
  167. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/tsinception.py +0 -0
  168. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/models/usleep.py +0 -0
  169. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/activation.py +0 -0
  170. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/channels.py +0 -0
  171. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/convolution.py +0 -0
  172. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/linear.py +0 -0
  173. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/linear_attention.py +0 -0
  174. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/parametrization.py +0 -0
  175. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/quantization.py +0 -0
  176. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/stats.py +0 -0
  177. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/util.py +0 -0
  178. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/modules/wrapper.py +0 -0
  179. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/preprocessing/__init__.py +0 -0
  180. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/preprocessing/util.py +0 -0
  181. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/samplers/__init__.py +0 -0
  182. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/samplers/ssl.py +0 -0
  183. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/training/callbacks.py +0 -0
  184. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/training/losses.py +0 -0
  185. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/visualization/__init__.py +0 -0
  186. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/visualization/activations.py +0 -0
  187. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/visualization/attribution.py +0 -0
  188. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/visualization/confusion_matrices.py +0 -0
  189. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/visualization/metrics.py +0 -0
  190. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/visualization/sanity.py +0 -0
  191. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode/visualization/topology.py +0 -0
  192. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode.egg-info/dependency_links.txt +0 -0
  193. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/braindecode.egg-info/top_level.txt +0 -0
  194. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/Makefile +0 -0
  195. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/_templates/autosummary/class.rst +0 -0
  196. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
  197. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/_templates/autosummary/function.rst +0 -0
  198. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
  199. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/agent_docs.rst +0 -0
  200. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/cite.rst +0 -0
  201. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/conf.py +0 -0
  202. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/help.rst +0 -0
  203. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/install/install_pip.rst +0 -0
  204. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/install/install_source.rst +0 -0
  205. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/attention.rst +0 -0
  206. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/channel.rst +0 -0
  207. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/convolution.rst +0 -0
  208. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/filterbank.rst +0 -0
  209. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/gnn.rst +0 -0
  210. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/interpretable.rst +0 -0
  211. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/lbm.rst +0 -0
  212. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/recurrent.rst +0 -0
  213. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/categorization/spd.rst +0 -0
  214. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/models.rst +0 -0
  215. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/models_categorization.rst +0 -0
  216. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/models_table.rst +0 -0
  217. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/models/models_visualization.rst +0 -0
  218. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/sg_execution_times.rst +0 -0
  219. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/docs/user_guide/channel_strategies.rst +0 -0
  220. {braindecode-1.8.1.dev169692045 → braindecode-1.8.1.dev169754522}/setup.cfg +0 -0
@@ -47,7 +47,6 @@ The following components are licensed under the MIT License:
47
47
  - `braindecode/models/mirepnet.py`
48
48
  - `braindecode/models/popt.py`
49
49
  - `braindecode/models/tfm_tokenizer.py`
50
- - `braindecode/modules/popt_modules.py`
51
50
  - `braindecode/models/brainomni.py`
52
51
  - `braindecode/modules/quantization.py`
53
52
  - `braindecode/modules/linear_attention.py`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: braindecode
3
- Version: 1.8.1.dev169692045
3
+ Version: 1.8.1.dev169754522
4
4
  Summary: Deep learning software to decode EEG, ECG or MEG signals
5
5
  Author-email: Robin Tibor Schirrmeister <robintibor@gmail.com>, Bruno Aristimunha Pinto <b.aristimunha@gmail.com>, Alexandre Gramfort <agramfort@meta.com>
6
6
  Maintainer-email: Alexandre Gramfort <agramfort@meta.com>, Bruno Aristimunha Pinto <b.aristimunha@gmail.com>, Robin Tibor Schirrmeister <robintibor@gmail.com>
@@ -22,8 +22,8 @@ Requires-Python: >=3.11
22
22
  Description-Content-Type: text/x-rst
23
23
  License-File: LICENSE.txt
24
24
  License-File: NOTICE.txt
25
- Requires-Dist: torch>=2.4
26
- Requires-Dist: torchaudio>=2.4
25
+ Requires-Dist: torch>=2.6
26
+ Requires-Dist: torchaudio>=2.6
27
27
  Requires-Dist: mne>=1.11.0
28
28
  Requires-Dist: mne_bids>=0.18
29
29
  Requires-Dist: h5py
@@ -32,8 +32,8 @@ Requires-Dist: joblib
32
32
  Requires-Dist: torchinfo
33
33
  Requires-Dist: pandas
34
34
  Requires-Dist: wfdb>=4.3.1
35
- Requires-Dist: linear_attention_transformer
36
35
  Requires-Dist: docstring_inheritance
36
+ Requires-Dist: einops
37
37
  Requires-Dist: pydantic>=2.0
38
38
  Provides-Extra: moabb
39
39
  Requires-Dist: moabb>=1.4.3; extra == "moabb"
@@ -50,6 +50,7 @@ Provides-Extra: tests
50
50
  Requires-Dist: pytest<9.1; extra == "tests"
51
51
  Requires-Dist: pytest-cov; extra == "tests"
52
52
  Requires-Dist: pytest-xdist>=3.8.0; extra == "tests"
53
+ Requires-Dist: pytest-timeout; extra == "tests"
53
54
  Requires-Dist: codecov; extra == "tests"
54
55
  Requires-Dist: pytest_cases; extra == "tests"
55
56
  Requires-Dist: mypy; extra == "tests"
@@ -0,0 +1,53 @@
1
+ import importlib
2
+ from typing import TYPE_CHECKING
3
+
4
+ from .version import __version__
5
+
6
+ if TYPE_CHECKING: # type checkers and IDEs see the classes; runtime uses __getattr__
7
+ from .classifier import EEGClassifier
8
+ from .regressor import EEGRegressor
9
+
10
+ __all__ = [
11
+ "__version__",
12
+ "EEGClassifier",
13
+ "EEGRegressor",
14
+ ]
15
+
16
+ # Subpackages reachable as ``braindecode.<name>`` after a bare ``import braindecode``.
17
+ _SUBMODULES = frozenset(
18
+ {
19
+ "augmentation",
20
+ "classifier",
21
+ "datasets",
22
+ "datautil",
23
+ "eegneuralnet",
24
+ "functional",
25
+ "models",
26
+ "modules",
27
+ "preprocessing",
28
+ "regressor",
29
+ "samplers",
30
+ "training",
31
+ "util",
32
+ "visualization",
33
+ }
34
+ )
35
+
36
+ if not TYPE_CHECKING: # hidden from checkers so misspelled names stay errors
37
+
38
+ def __getattr__(name):
39
+ # PEP 562: skorch, the datasets and the models load on first use.
40
+ if name == "EEGClassifier":
41
+ from .classifier import EEGClassifier
42
+
43
+ return EEGClassifier
44
+ if name == "EEGRegressor":
45
+ from .regressor import EEGRegressor
46
+
47
+ return EEGRegressor
48
+ if name in _SUBMODULES:
49
+ return importlib.import_module(f".{name}", __name__)
50
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
51
+
52
+ def __dir__():
53
+ return sorted(set(globals()) | set(__all__) | _SUBMODULES)
@@ -9,10 +9,11 @@
9
9
  from numbers import Real
10
10
  from typing import Any, Callable, Optional, Union
11
11
 
12
+ import numpy as np
12
13
  import torch
13
14
  from sklearn.utils import check_random_state
14
15
  from torch import Tensor, nn
15
- from torch.utils.data import DataLoader
16
+ from torch.utils.data import DataLoader, get_worker_info
16
17
  from torch.utils.data._utils.collate import default_collate
17
18
 
18
19
  from .functional import identity
@@ -191,6 +192,59 @@ def _as_mixed_target(y, lam_dtype):
191
192
  return y, y, lam
192
193
 
193
194
 
195
+ def _transform_rngs(transform):
196
+ """Return the ``RandomState`` objects of a transform, in a fixed order.
197
+
198
+ Walks the transform, its submodules and the ``transforms`` of a
199
+ :class:`Compose`, and lists each ``rng`` once (pooled transforms of
200
+ :class:`braindecode.augmentation.TrivialAugment` share theirs). The global
201
+ numpy generator, used with ``random_state=None``, is included: a spawned
202
+ worker receives a pickled copy of it, which torch does not reseed.
203
+ """
204
+ rngs, seen, stack = [], set(), [transform]
205
+ while stack:
206
+ tr = stack.pop(0)
207
+ if id(tr) in seen:
208
+ continue
209
+ seen.add(id(tr))
210
+ # Compose forwards to its children without using its inherited RNG.
211
+ rng = None if type(tr) is Compose else getattr(tr, "rng", None)
212
+ if isinstance(rng, np.random.RandomState) and id(rng) not in seen:
213
+ seen.add(id(rng))
214
+ rngs.append(rng)
215
+ children = list(getattr(tr, "transforms", None) or [])
216
+ if isinstance(tr, nn.Module):
217
+ children += list(tr.children())
218
+ stack.extend(children)
219
+ return rngs
220
+
221
+
222
+ def _assert_stackable(items, name):
223
+ # Fixed expansion concatenates the clean originals with the transformed
224
+ # copies, so every copy must keep the original's shape except along the
225
+ # batch dimension. A transform that reshapes X or y (e.g. one returning
226
+ # class distributions instead of class indices) would otherwise fail deep
227
+ # inside torch.cat with a bare dimension-mismatch RuntimeError.
228
+ reference = items[0]
229
+ for item in items[1:]:
230
+ pairs = (
231
+ zip(reference, item)
232
+ if isinstance(reference, (tuple, list))
233
+ else [(reference, item)]
234
+ )
235
+ for ref_part, part in pairs:
236
+ if part.dim() != ref_part.dim() or part.shape[1:] != ref_part.shape[1:]:
237
+ raise ValueError(
238
+ f"The transform returned {name} with shape "
239
+ f"{tuple(part.shape)} instead of {tuple(ref_part.shape)}: "
240
+ f"the fixed-expansion mode (n_augmentation > 0) keeps the "
241
+ f"clean originals and concatenates the transformed "
242
+ f"copies, so it cannot accommodate a transform that "
243
+ f"changes the shape of {name}. Use n_augmentation = 0 "
244
+ f"(in-place augmentation) instead."
245
+ )
246
+
247
+
194
248
  class _AugmentationCollate:
195
249
  """Collate that applies a transform to each batch, with optional expansion.
196
250
 
@@ -211,15 +265,44 @@ class _AugmentationCollate:
211
265
  ``(1 + n_augmentation)`` times the original size. When the transform
212
266
  mixes targets, as :class:`braindecode.augmentation.Mixup` does, ``y``
213
267
  stays the ``(y_a, y_b, lam)`` triple and the clean originals get a
214
- mixing coefficient of one.
268
+ mixing coefficient of one. In this mode a transform that changes the
269
+ shape of ``X`` or ``y`` is rejected with a clear
270
+ :class:`ValueError`, as its copies cannot be concatenated with the
271
+ clean originals.
215
272
  """
216
273
 
217
274
  def __init__(self, transform, device=None, n_augmentation=0):
218
275
  self.transform = transform
219
276
  self.device = device
220
277
  self.n_augmentation = n_augmentation
278
+ self.worker_seeds = None
279
+ self._worker_rngs_seeded = False
280
+
281
+ def draw_worker_seeds(self):
282
+ """Draw, from each generator of the transform, a seed for the workers.
283
+
284
+ Called in the main process when a multi-process iteration starts. A
285
+ worker receives a copy of the transform whose generators are in the
286
+ state of the main process; without a new seed, every worker, and every
287
+ epoch, would draw the same augmentations.
288
+ """
289
+ self.worker_seeds = [
290
+ int(rng.randint(2**31)) for rng in _transform_rngs(self.transform)
291
+ ]
292
+
293
+ def _seed_worker_rngs(self):
294
+ if self._worker_rngs_seeded or self.worker_seeds is None:
295
+ return
296
+ worker_info = get_worker_info()
297
+ if worker_info is None:
298
+ return
299
+ self._worker_rngs_seeded = True
300
+ rngs = _transform_rngs(self.transform)
301
+ for rng, seed in zip(rngs, self.worker_seeds):
302
+ rng.seed(np.random.SeedSequence([seed, worker_info.id]).generate_state(1))
221
303
 
222
304
  def __call__(self, batch):
305
+ self._seed_worker_rngs()
223
306
  collated = default_collate(batch)
224
307
  X, y = collated[:2]
225
308
  if self.device is not None:
@@ -239,13 +322,16 @@ class _AugmentationCollate:
239
322
  for aug_y in ys
240
323
  if isinstance(aug_y, (tuple, list)) and len(aug_y) == 3
241
324
  ]
325
+ _assert_stackable(xs, "X")
242
326
  if mixed_ys:
243
327
  # a target-mixing transform such as Mixup returns (y_a, y_b, lam),
244
328
  # so the parts are concatenated one by one and the untouched copies
245
329
  # are given a mixing coefficient of one
246
330
  lam_dtype = mixed_ys[0][2].dtype
247
331
  ys = [_as_mixed_target(aug_y, lam_dtype) for aug_y in ys]
332
+ _assert_stackable(ys, "y")
248
333
  return torch.cat(xs), tuple(torch.cat(part) for part in zip(*ys))
334
+ _assert_stackable(ys, "y")
249
335
  return torch.cat(xs), torch.cat(ys)
250
336
 
251
337
 
@@ -268,7 +354,10 @@ class AugmentedDataLoader(DataLoader):
268
354
  the clean originals are kept and ``n_augmentation`` independently
269
355
  transformed copies are appended. This expresses augmentations defined as
270
356
  a fixed set-expansion (e.g. the EEG-Inception MI 6x training set,
271
- ``n_augmentation=5``). In this mode batches are returned as ``(X, y)``.
357
+ ``n_augmentation=5``). In this mode batches are returned as ``(X, y)``
358
+ and transforms that change the shape of ``X`` or ``y`` are rejected
359
+ with a :class:`ValueError`, as their copies cannot be concatenated
360
+ with the clean originals.
272
361
  **kwargs : dict, optional
273
362
  keyword arguments to pass to standard DataLoader class.
274
363
  """
@@ -301,3 +390,10 @@ class AugmentedDataLoader(DataLoader):
301
390
  transform, device=device, n_augmentation=n_augmentation
302
391
  )
303
392
  super().__init__(dataset, collate_fn=self.collated_tr, **kwargs)
393
+
394
+ def __iter__(self):
395
+ # the workers get a copy of the transform; give them new seeds so that
396
+ # seeded transforms differ across workers and epochs
397
+ if self.num_workers > 0:
398
+ self.collated_tr.draw_worker_seeds()
399
+ return super().__iter__()
@@ -1089,10 +1089,12 @@ def mixup(
1089
1089
  Online: https://arxiv.org/abs/1710.09412
1090
1090
  .. [2] https://github.com/facebookresearch/mixup-cifar10/blob/master/train.py
1091
1091
  """
1092
- device = X.device
1093
1092
  batch_size, n_channels, n_times = X.shape
1094
1093
 
1095
- X_mix = torch.zeros((batch_size, n_channels, n_times)).to(device)
1094
+ X_mix = X.new_zeros(
1095
+ (batch_size, n_channels, n_times),
1096
+ dtype=X.dtype if X.is_floating_point() else torch.get_default_dtype(),
1097
+ )
1096
1098
  y_a = y.clone()
1097
1099
  y_b = y[idx_perm].clone()
1098
1100
 
@@ -1173,7 +1173,8 @@ class LabelSmoothing(Transform):
1173
1173
  fixed-expansion mode (``n_augmentation > 0``) of
1174
1174
  :class:`braindecode.augmentation.AugmentedDataLoader` is not supported:
1175
1175
  it cannot concatenate the unsmoothed original targets with the reshaped
1176
- smoothed copies.
1176
+ smoothed copies and rejects the combination with a :class:`ValueError`
1177
+ pointing to ``n_augmentation = 0``.
1177
1178
 
1178
1179
  Parameters
1179
1180
  ----------
@@ -33,7 +33,12 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
33
33
 
34
34
  cropped: bool (default=False)
35
35
  Defines whether torch model passed to this class is cropped or not.
36
- Currently used for callbacks definition.
36
+ Currently used for callbacks definition. For input without window
37
+ indices (such as numpy arrays), scoring callbacks treat each example
38
+ as a separate trial and average its temporal predictions. To aggregate
39
+ multiple windows of the same trial, pass a window dataset retaining
40
+ its window indices. Cross-validation splits must keep windows from
41
+ the same trial together.
37
42
 
38
43
  callbacks: None or list of strings or list of Callback instances (default=None)
39
44
  More callbacks, in addition to those returned by
@@ -195,7 +200,7 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
195
200
  """
196
201
  return self.predict_proba(X).argmax(1)
197
202
 
198
- def predict_trials(self, X, return_targets=True):
203
+ def predict_trials(self, X, return_targets=True, *, loader_kwargs=None):
199
204
  """Create trialwise predictions from a cropped dataset.
200
205
 
201
206
  Parameters
@@ -204,6 +209,9 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
204
209
  A braindecode dataset to be predicted.
205
210
  return_targets : bool
206
211
  If True, additionally returns the trial targets.
212
+ loader_kwargs : dict | None
213
+ Cropped mode only: DataLoader overrides, see
214
+ :func:`braindecode.training.iter_trial_predictions`.
207
215
 
208
216
  Returns
209
217
  -------
@@ -220,6 +228,8 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
220
228
  trial lengths differ, a list with one target array per trial.
221
229
  """
222
230
  if not self.cropped:
231
+ if loader_kwargs is not None:
232
+ raise ValueError("loader_kwargs requires cropped=True")
223
233
  warnings.warn(
224
234
  "This method was designed to predict trials in cropped mode. "
225
235
  "Calling it when cropped is False will give the same result as "
@@ -237,6 +247,7 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
237
247
  return_targets=return_targets,
238
248
  batch_size=self.batch_size,
239
249
  num_workers=self.get_iterator(X, training=False).loader.num_workers,
250
+ loader_kwargs=loader_kwargs,
240
251
  )
241
252
 
242
253
  @property
@@ -26,6 +26,7 @@ from itertools import chain
26
26
  from operator import attrgetter
27
27
  from pathlib import Path
28
28
  from typing import Any, Generic, Iterable, no_type_check
29
+ from uuid import uuid4
29
30
 
30
31
  import mne
31
32
  import mne.io
@@ -343,6 +344,9 @@ def _read_fif_window(raw, start, stop, willneed=False):
343
344
  return np.multiply(picked, raw._cals[:, np.newaxis], order="C")
344
345
 
345
346
 
347
+ _COALESCED_MAX_BYTES = 2 * 1024 * 1024
348
+
349
+
346
350
  def _window_info(crop_inds, sfreq):
347
351
  """Extract window size from crop indices.
348
352
 
@@ -479,14 +483,14 @@ def _zarr_to_memmap(zarr_path, group_name):
479
483
  ``SIGBUS`` when workers page-faulted on the unlinked inode.
480
484
 
481
485
  Under ``N`` concurrent writers, every racer materialises the data
482
- into its own per-pid temp file and then tries to publish it; at
486
+ into its own per-call temp file and then tries to publish it; at
483
487
  most one ``os.link`` wins, and the losers silently ``unlink``
484
488
  their temp file in a ``finally`` block. The wasted I/O scales
485
489
  with the number of racers; for the workloads braindecode targets
486
490
  (a handful of concurrent workers per group) it is negligible.
487
491
 
488
492
  Crash recovery: if a writer dies mid-run the only debris is a
489
- ``<group>.<pid>.tmp.npy`` file. Such files are namespaced by pid
493
+ ``<group>.<uuid>.tmp.npy`` file. Such files are unique per call
490
494
  so they can never collide with a live writer, they are never
491
495
  read by the fast path, and they do not prevent subsequent
492
496
  callers from producing a correct result. Caches written by
@@ -504,7 +508,10 @@ def _zarr_to_memmap(zarr_path, group_name):
504
508
 
505
509
  cache_dir.mkdir(parents=True, exist_ok=True)
506
510
  arr = zarr.open(zarr_path, mode="r")[group_name]["data"]
507
- tmp_path = cache_dir / f"{group_name}.{os.getpid()}.tmp.npy"
511
+ # Unique per call, not per pid: threads of one process must not share it.
512
+ # Exclusive creation, not tempfile.mkstemp, which would make it 0600.
513
+ tmp_path = cache_dir / f"{group_name}.{uuid4().hex}.tmp.npy"
514
+ tmp_path.open("xb").close()
508
515
  try:
509
516
  # Write chunk-by-chunk to avoid materialising the whole zarr
510
517
  # array in RAM.
@@ -531,9 +538,9 @@ def _zarr_to_memmap(zarr_path, group_name):
531
538
  # canonical one; ours is discarded in the finally block.
532
539
  pass
533
540
  finally:
534
- # Always remove our private per-pid temp file. It is
535
- # namespaced by pid so this cannot clobber a concurrent
536
- # writer's in-progress tmp file.
541
+ # Always remove our private per-call temp file. Its name is
542
+ # unique per call so this cannot clobber a concurrent writer's
543
+ # in-progress tmp file.
537
544
  try:
538
545
  os.unlink(tmp_path)
539
546
  except FileNotFoundError:
@@ -953,6 +960,10 @@ class EEGWindowsDataset(_ZarrMixin, RecordDataset):
953
960
  X = self.raw._getitem(
954
961
  (slice(None), slice(i_start, i_stop)), return_times=False
955
962
  )
963
+ return self._finish_window(index, crop_inds, X)
964
+
965
+ def _finish_window(self, index, crop_inds, X):
966
+ """Item ``index`` from its raw samples ``X``, copied before the transform."""
956
967
  X = np.array(X, dtype="float32")
957
968
  if self.transform is not None:
958
969
  X = self.transform(X)
@@ -1344,11 +1355,24 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
1344
1355
  Optional function to call on targets before returning them.
1345
1356
  lazy : bool, default False
1346
1357
  If True, defer computing cumulative sizes until length or item access.
1358
+ coalesce_reads : bool, default False
1359
+ Opt in to reading a :class:`~torch.utils.data.DataLoader` batch of
1360
+ consecutive, overlapping windows of one lazy (``preload=False``) FIF
1361
+ recording as one union of samples instead of one read per window.
1362
+ Only a batch of consecutive ascending indices into one
1363
+ :class:`~braindecode.datasets.EEGWindowsDataset` (not a subclass)
1364
+ whose union is at most a third of the summed window samples and at
1365
+ most 2 MiB as float64 is read this way; every other batch takes the
1366
+ default path. Outputs, order and random number use equal the
1367
+ default's. It was faster only for heavily overlapping windows and
1368
+ slower for some other layouts (numbers in :ref:`whats_new`), so it is off
1369
+ by default. Containers returned by ``split`` default to ``False``.
1347
1370
  """
1348
1371
 
1349
1372
  plot = _viewer_plot # eegdash-viewer embed; a direct member so the API docs list it
1350
1373
 
1351
1374
  datasets: list[T]
1375
+ coalesce_reads: bool = False # default for pickles made before this option
1352
1376
 
1353
1377
  def __init__(
1354
1378
  self,
@@ -1358,6 +1382,7 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
1358
1382
  target_transform: Callable | None = None,
1359
1383
  *,
1360
1384
  lazy: bool = False,
1385
+ coalesce_reads: bool = False,
1361
1386
  ):
1362
1387
  # Adapted from torch.utils.data.ConcatDataset:
1363
1388
  # added lazy init to defer cumulative size computation until access,
@@ -1387,6 +1412,9 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
1387
1412
 
1388
1413
  self.target_transform = target_transform
1389
1414
  self._lazy = lazy
1415
+ if type(coalesce_reads) is not bool:
1416
+ raise TypeError("coalesce_reads must be a bool.")
1417
+ self.coalesce_reads = coalesce_reads
1390
1418
 
1391
1419
  @property
1392
1420
  def cumulative_sizes(self) -> list[int]:
@@ -1454,6 +1482,10 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
1454
1482
  another: on a network file system a batch read from cold files then
1455
1483
  costs about one round trip, not one per window.
1456
1484
  """
1485
+ if self.coalesce_reads:
1486
+ coalesced = self._get_coalesced_items(indices)
1487
+ if coalesced is not None:
1488
+ return coalesced
1457
1489
  # ponytail: advises the whole batch at once; chunk it if a batch's
1458
1490
  # windows ever outgrow the page cache
1459
1491
  if hasattr(os, "posix_fadvise"):
@@ -1466,6 +1498,52 @@ class BaseConcatDataset(ConcatDataset, HubDatasetMixin, Generic[T]):
1466
1498
  _read_fif_window(ds.raw, start, stop, willneed=True)
1467
1499
  return [self[idx] for idx in indices]
1468
1500
 
1501
+ def _get_coalesced_items(self, indices):
1502
+ """The items cut from one read of their union, or None if not eligible."""
1503
+ n = len(indices)
1504
+ if (
1505
+ type(self) is not BaseConcatDataset # subclasses may override items
1506
+ or n < 2
1507
+ or type(indices[0]) is not int
1508
+ or indices[0] < 0
1509
+ or indices != list(range(indices[0], indices[0] + n))
1510
+ ):
1511
+ return None
1512
+ sizes = self._ensure_cumulative_sizes()
1513
+ k = bisect.bisect_right(sizes, indices[0])
1514
+ if k == len(sizes) or indices[-1] >= sizes[k]:
1515
+ return None # past the end, or across recordings
1516
+ ds = self.datasets[k]
1517
+ if (
1518
+ type(ds) is not EEGWindowsDataset
1519
+ or ds._zarr_data is not None
1520
+ or not isinstance(ds.crop_inds, np.ndarray) # lazy metadata
1521
+ or ds.raw.preload
1522
+ ):
1523
+ return None
1524
+ first = indices[0] - (sizes[k - 1] if k else 0)
1525
+ crops = ds.crop_inds[first : first + n]
1526
+ start, stop = int(crops[:, 1].min()), int(crops[:, 2].max())
1527
+ if (
1528
+ start < 0
1529
+ or stop > ds.raw.n_times
1530
+ or (crops[:, 2] <= crops[:, 1]).any()
1531
+ or 3 * (stop - start) > (crops[:, 2] - crops[:, 1]).sum()
1532
+ or (stop - start) * ds.raw.info["nchan"] * 8 > _COALESCED_MAX_BYTES
1533
+ ):
1534
+ return None
1535
+ union = _read_fif_window(ds.raw, start, stop)
1536
+ if union is None:
1537
+ return None
1538
+ items = []
1539
+ for i, crop in enumerate(crops.tolist(), first):
1540
+ X = union[:, crop[1] - start : crop[2] - start]
1541
+ item = ds._finish_window(i, crop, X)
1542
+ if self.target_transform is not None:
1543
+ item = item[:1] + (self.target_transform(item[1]),) + item[2:]
1544
+ items.append(item)
1545
+ return items
1546
+
1469
1547
  def __getitem__(self, idx: int | list):
1470
1548
  """
1471
1549
  ---
@@ -224,15 +224,39 @@ class BIDSEpochsDataset(BIDSDataset):
224
224
  )
225
225
 
226
226
  def _set_metadata(self, epochs: mne.BaseEpochs) -> None:
227
- # events = mne.events_from_annotations(epochs
228
227
  n_times = epochs.times.shape[0]
229
- # id_event = {v: k for k, v in epochs.event_id.items()}
230
- annotations = epochs.annotations
231
- if annotations is not None:
232
- target = annotations.description
233
- else:
234
- id_events = {v: k for k, v in epochs.event_id.items()}
235
- target = [id_events[event_id] for event_id in epochs.events[:, -1]]
228
+ id_events = {v: k for k, v in epochs.event_id.items()}
229
+ target = [id_events[event_id] for event_id in epochs.events[:, -1]]
230
+ if epochs.annotations is not None and len(epochs.annotations) > 0:
231
+ # Match triggers on the original recording's sample clock, even if
232
+ # epochs have been cropped past time zero, decimated or resampled.
233
+ onset_samples = np.round(
234
+ epochs.annotations.onset * epochs._raw_sfreq
235
+ ).astype(int)
236
+ at_zero = []
237
+ for sample in epochs.events[:, 0]:
238
+ matches = np.flatnonzero(onset_samples == sample)
239
+ if len(matches) == 0:
240
+ # FIF stores onsets as float32. Only the quantization bins
241
+ # containing this sample's half-sample boundaries can move
242
+ # a valid onset to an adjacent rounded sample on readback.
243
+ boundaries = np.asarray(
244
+ [
245
+ (sample - 0.5) / epochs._raw_sfreq,
246
+ (sample + 0.5) / epochs._raw_sfreq,
247
+ ],
248
+ dtype=np.float32,
249
+ ).astype(float)
250
+ matches = np.flatnonzero(
251
+ np.isin(epochs.annotations.onset, boundaries)
252
+ & (np.abs(onset_samples - sample) == 1)
253
+ )
254
+ at_zero.append(matches)
255
+ if all(len(matches) == 1 for matches in at_zero):
256
+ indices = [matches[0] for matches in at_zero]
257
+ # A boundary annotation cannot label two neighboring events.
258
+ if len(set(indices)) == len(set(epochs.events[:, 0])):
259
+ target = epochs.annotations.description[indices].tolist()
236
260
  metadata_dict = {
237
261
  "i_window_in_trial": np.zeros(len(epochs)),
238
262
  "i_start_in_trial": np.zeros(len(epochs)),
@@ -16,12 +16,7 @@ It reorganizes the file structure to comply with the BIDS specification. To this
16
16
  # License: BSD (3-clause)
17
17
  from __future__ import annotations
18
18
 
19
- from pathlib import Path
20
-
21
- from mne.datasets import fetch_dataset
22
-
23
- from braindecode.datasets import BIDSDataset
24
- from braindecode.datasets.utils import _correct_dataset_path
19
+ from braindecode.datasets.utils import _ZenodoBIDSDataset
25
20
 
26
21
  CHB_MIT_URL = "https://zenodo.org/records/10259996/files/BIDS_CHB-MIT.zip"
27
22
  CHB_MIT_archive_name = "chb_mit_bids.zip"
@@ -38,7 +33,7 @@ CHB_MIT_dataset_params = {
38
33
  }
39
34
 
40
35
 
41
- class CHBMIT(BIDSDataset):
36
+ class CHBMIT(_ZenodoBIDSDataset):
42
37
  """The Children's Hospital Boston EEG Dataset.
43
38
 
44
39
  This database, collected at the Children's Hospital Boston, consists of EEG recordings
@@ -129,35 +124,6 @@ class CHBMIT(BIDSDataset):
129
124
  .. footbibliography::
130
125
  """
131
126
 
132
- def __init__(self, root=None, *args, **kwargs):
133
- # Download dataset if not present
134
- if root is None:
135
- path_root = fetch_dataset(
136
- dataset_params=CHB_MIT_dataset_params,
137
- path=None,
138
- processor="unzip",
139
- force_update=False,
140
- )
141
- # First time we fetch the dataset, we need to move the files to the
142
- # correct directory.
143
- path_root = _correct_dataset_path(
144
- path_root, CHB_MIT_archive_name, "BIDS_CHB-MIT"
145
- )
146
- else:
147
- # Validate that the provided root is a valid BIDS dataset
148
- if not Path(f"{root}/participants.tsv").exists():
149
- raise ValueError(
150
- f"The provided root directory {root} does not contain a valid "
151
- "BIDS dataset (missing participants.tsv). Please ensure the "
152
- "root points directly to the BIDS dataset directory."
153
- )
154
- path_root = root
155
-
156
- kwargs["root"] = path_root
157
-
158
- super().__init__(
159
- *args,
160
- extensions=".edf",
161
- check=False,
162
- **kwargs,
163
- )
127
+ _dataset_params = CHB_MIT_dataset_params
128
+ _archive_name = CHB_MIT_archive_name
129
+ _unzipped_dir = "BIDS_CHB-MIT"