What’s new#

Current 1.8.1 (2026-08-31)#

Enhancements#

  • Add braindecode.models.TFMTokenizer, the time-frequency motif tokenizer for single-channel EEG of Pradeepkumar et al. (ICLR 2026) (#1202 by lindicaphxag-tech).

  • The pretrained-compatibility test now covers every model class with released weights (NeuroRVQ, MAPA, BrainOmni, BrainTokenizer and the SignalJEPA heads added), checks that the list is complete, and runs REVE’s cases without network access (#1252 by Bruno Aristimunha)

  • braindecode.models.NeuroRVQ now reuses the LaBraM attention block instead of a private copy, and the K-means codebook initialisation in braindecode.modules.quantization uses torch.cdist (about 6x faster, lower peak memory, identical codebooks). Outputs and state-dict keys are unchanged. (by Bruno Aristimunha)

  • Add braindecode.models.MAPA, a masked-autoencoder foundation model for intracranial EEG that describes an electrode only by its atlas region and its number along the array it was implanted on, never by its coordinates, so that one pretrained encoder reads a subject it has never seen: three magnitude-spectrogram bands on a shared frame clock are tokenized per contact, offset by a learned region embedding, and mixed by a transformer whose attention stays inside one array and carries a two-axis rotary encoding on the contact number and on time. One instance encodes recordings from different subjects, by passing each one’s electrode metadata to forward, and normalization="session" takes a spectrogram normalized over the whole recording, which reproduces the reference inputs (#1178 by Julien Gadonneix).

  • braindecode.models.MAPA loads the released mapa_vits384 with MAPA.from_pretrained("braindecode/mapa-pretrained", ...); the key mapping for the original checkpoint is removed (by Bruno Aristimunha).

  • Add test/unit_tests/models/test_pretrained_compat.py: every model with released weights is built on a grid of input geometries (canonical montage, permuted order, a 64-channel montage outside the 10-20 vocabulary, coordinates-only channels, names without coordinates, 1 s / 30 s / non-divisible windows) and must forward or raise the error its declared channel strategy implies; unhandled cells are strict xfail markers (#1228 by Bruno Aristimunha).

  • Add channel_strategy ("exact", "zero", "nearest", "idw", "spline", "field", "source", "region", "wiener", "latent"; default "native") and channel_strategy_kwargs to the 19 pretrained models, saved in the config. A braindecode.modules.ChannelLayer (one matrix per montage) maps any montage onto the channels the backbone consumes, also per call with model(x, chs_info=...). "native" keeps every released checkpoint bit-identical with the same state_dict. It replaces the unreleased ChannelTokenizer; BENDR adapts a non-canonical montage with an MNE spline and SignalJEPA no longer returns NaN for names without coordinates. See Channel strategies: any montage in (#1227, #1241 by Bruno Aristimunha).

  • Add registry-wide model contract tests that automatically cover every registered model, checking eval-mode input/state purity, finite batched outputs, batch-permutation equivariance, and get_config + state_dict reconstruction (#1208 by lindicaphxag-tech).

  • Add braindecode.models.NeuroRVQ, a channel-aware EEG foundation model with four-scale temporal patch embedding and a pretrained masked-token encoder. The port preserves the released architecture and identifies its CC BY-NC 4.0 license and 200 Hz preprocessing requirements (#1090 by lindicaphxag-tech).

  • Generate a version-scoped llms.txt and selected Markdown documentation entry points with source-commit attribution and critical-page coverage checks.

  • Add braindecode.models.NeuroRVQTokenizer, the released NeuroRVQ EEG tokenizer: reconstruction and discrete codes (#1223 by lindicaphxag-tech).

  • Add braindecode.models.AXON, an axis-factorized EEG foundation model whose layers mix a temporal and a spatial attention path with a per-token gate, with pretrained weights on the Hugging Face Hub (#1182 by Mahir Jain).

  • braindecode.models.NeuroRVQ and braindecode.models.NeuroRVQTokenizer take modality ("eeg", "ecg", "emg", "ppg"), the settings of the authors’ four released packages; the seven released checkpoints load with from_pretrained from braindecode/neurorvq-{eeg,ecg,emg}-pretrained and braindecode/neurorvq-tokenizer-{eeg,ecg,emg,ppg}-pretrained (load_pretrained_weights is removed). The tokenizer reconstruction now equals the authors’ code (it differed by up to 6e-8); statistic_code_usage=True reproduces their eval-time code-usage EMA. Builds on #1090 and #1223 by lindicaphxag-tech (#1254 by Bruno Aristimunha).

  • Add braindecode.models.TMSANet, the motor-imagery convolution and local/global attention model of Zhao and Zhu (2025) (#1209 by lindicaphxag-tech).

  • Add braindecode.models.SeizureTransformer, the U-shaped convolution and Transformer seizure detector of Wu et al. (2025) that won the 2025 SzCORE seizure detection challenge. It predicts a logit for every time sample. With the authors’ released weights it reproduces their challenge scores on braindecode.datasets.SIENA (event F1 0.706) (#1236 by Raghav Rathi).

  • Restore acceptance tests on supported Python versions as seeded decoding checks on BNCI2014_001 (held-out accuracy thresholds, a shuffled-label control and a replicability check), run by a dedicated CI job (#1159 by Bruno Aristimunha).

  • Clarify decoder temporal embedding indexing in braindecode.models.Labram and cover its one-token-per-temporal-patch behavior (#1155 by Bruno Aristimunha).

  • Add braindecode.models.SleepFM, a channel-agnostic multimodal PSG foundation encoder, and braindecode.models.SleepFMStager, its patch-wise sleep-staging model, with the released weights; CC BY-NC 4.0 (#1106 by Fashad Ahmed)

  • Add braindecode.models.CSBrain, the cross-scale spatiotemporal brain foundation model from Zhou et al. (NeurIPS 2025 Spotlight): multi-scale temporal and per-region embeddings with structured sparse (inter-window and inter-region) attention, channel names mapped to five anatomical regions or an explicit brain_regions layout, verified bit-exact against the authors’ released pretrained checkpoint (#1196 by Li Qing).

  • braindecode.models.CSBrain reuses the patch embedding of braindecode.models.CBraMod instead of a copy of it; state-dict keys, outputs with loaded weights and same-seed initial weights are unchanged (#1240 by Bruno Aristimunha).

  • Add head_drop_prob to braindecode.models.CSBrain, the task-head dropout of the reference fine-tuning models (default: drop_prob, so existing models are unchanged), and document the checkpoint key names (#1247 by Bruno Aristimunha).

  • Add braindecode.models.DIVER1, an any-variate EEG/iEEG foundation model with pretrained encoders and support for varying montages through braindecode.models.diver1.channel_metadata_from_chs_info() (#1170 by Julien Gadonneix).

  • braindecode.models.ZUNA accepts an n_times that is not a multiple of fine_time_pts through the new on_non_divisible option ("pad" or "crop"), forwarded to the shared braindecode.modules.PatchTokenizer; the default "error" keeps the previous behaviour (#1190 by Bruno Aristimunha).

  • Add braindecode.models.MSCFormer, a multi-scale convolutional transformer network for motor imagery decoding from Zhao et al. (2025), adapted from the reference implementation to reuse braindecode’s shared attention and feed-forward blocks. The default attention logit scale reproduces the released source’s embed_dim ** -0.5 (numerically verified against the original code, max abs logit diff < 1e-6) and is configurable via attention_scale (#1186 by Li Qing).

  • Add braindecode.models.BaRISTA, an intracranial EEG foundation model whose spatial encoding scale is a free choice: electrodes are tokenized channel-wise and space enters as a single learned embedding selected by the electrode coordinate, its atlas parcel or its lobe, before a joint space-time transformer encoder with rotary temporal embeddings (#1173 by Julien Gadonneix).

  • Add braindecode.models.Brant, a braindecode-native port of the Brant foundation model for intracranial (sEEG/iEEG) signals (Zhang et al., NeurIPS 2023), including in-model spectral features and the shared configuration, feature-return, and head-reset APIs. The official pretrained weights load from braindecode/brant-pretrained (all tensors verified identical to the official release) (#1100 by Adam Mounir).

  • Add braindecode.models.BrainTokenizer, the EEG/MEG VQ-VAE tokenizer of BrainOmni (NeurIPS 2025), with the released weights converted to braindecode/braintokenizer-pretrained (#1043 by Bruno Aristimunha).

  • Add braindecode.models.PopulationTransformer (PopT, Chau et al. 2024), an iEEG population model over per-electrode features and coordinates, with pretrained weights at braindecode/popt-pretrained (#1105 by Adam Mounir).

  • Add braindecode.models.BrainOmni, the BrainOmni downstream classifier on a frozen braindecode.models.BrainTokenizer, with the released tiny and base weights converted to braindecode/brainomni-tiny-pretrained and braindecode/brainomni-base-pretrained (#1043 by Bruno Aristimunha).

  • Add braindecode.models.VEMG2Pose, braindecode.models.NeuroPose, and braindecode.models.SensingDynamics for dense hand-pose regression from surface EMG (#1132 by Bruno Aristimunha).

  • Add a bounded SAE intervention tutorial on a frozen pretrained REVE with BNCI2014_001 using optional SAE Lens, without adding an SAE implementation to Braindecode (#1120 by Vandit Shah and Bruno Aristimunha)

  • Improve CI test scheduling and run documentation examples with bounded parallelism, preserving the test cases and gallery training workloads (#1161 by Bruno Aristimunha).

  • Add a bounded SAE activation analysis tutorial on BCI IV 2a (BNCI2014_001) using optional SAE Lens, without adding an SAE implementation to Braindecode (#1152 by Vandit Shah and Bruno Aristimunha)

  • Add braindecode.models.BrainBERT, a self-supervised foundation model for intracranial (sEEG/iEEG) signals from Wang et al. (ICLR 2023), with pretrained weights (#1104 by Adam Mounir).

  • Add pull request templates, including an exhaustive checklist for new model contributions covering implementation conventions, registration, documentation, and benchmarking (#1169 by Li Qing).

API and behavior changes#

  • braindecode.models.Labram, braindecode.models.CBraMod and braindecode.models.LUNA tokenize the time axis with the shared braindecode.modules.PatchTokenizer and accept a window that is not a multiple of patch_size by right zero-padding the last patch (with a warning), through a new on_non_divisible={"pad", "crop", "error"} argument; previously such windows raised a reshape error. braindecode.models.ZUNA now defaults to on_non_divisible="pad" as well (it was "error"). Outputs and state-dict keys are unchanged for divisible windows, so released checkpoints load as before (#1226 by Bruno Aristimunha).

  • braindecode.models.Labram defaults to use_mean_pooling=True again, the documented value and the readout of the original fine-tuning (fc_norm of the mean patch token). #931 had made the [CLS] output, which the pretraining loss never uses, the default so that the pretraining checkpoint loaded strictly. That checkpoint, such as the released weights, now loads into the mean-pooling model as in the original fine-tuning script (norm unused, fc_norm initialized). Pass use_mean_pooling=False to keep the [CLS] readout, for instance to load a checkpoint fine-tuned with it, which no longer loads into the default model (#1155 by Bruno Aristimunha).

  • Remove InterpolatedBENDR, InterpolatedBIOT, InterpolatedEEGPT, InterpolatedLaBraM, InterpolatedSignalJEPA, InterpolatedModel, ChannelInterpolationLayer and interpolated_models_dict: use BENDR(chs_info=..., channel_strategy="spline") instead (#1241 by Bruno Aristimunha).

Requirements#

  • Add sphinx-markdown-builder==0.6.11 and pytest<9.1 to the docs extra for version-scoped Markdown exports and their offline contract tests.

  • Require PyTorch and TorchAudio >= 2.4 and remove obsolete attention fallbacks. REVE and ZUNA now import PyTorch’s RMSNorm layer directly, preserving their explicit epsilon values. Intel macOS is no longer supported because PyTorch stopped providing its binary packages after 2.2. (#1174 by Bruno Aristimunha)

Bug fixes#

  • Model fixes caught by new CPU-only integration checks (complex tensors, host syncs, kernel gaps, device/dtype follow, training after inference_mode, deepcopy/pickle): BrainOmni/BrainTokenizer SELU trains on Gaudi, EEGSym pools with avg_pool2d, FBCNet/FBMSNet/FBLightConvNet and LUNA run in float16, EEGMiner and AttnSleep deep-copy after training, Labram and NeuroRVQ pickle, SignalJEPA heads accept channel_strategy, and tensors built in forward follow the input in CodeBrain, TCFormer, LUNA, MVPFormer, BrainOmni, ZUNA, DIVER1 and EEGDINO; float32 outputs unchanged (#1253 by Bruno Aristimunha)

  • braindecode.models.BrainOmni and braindecode.models.BrainTokenizer now run forward on Intel Gaudi (HPU) in lazy mode: the SEANet LSTM input is permuted as a 4D view, which Gaudi compiles; values are unchanged (#1249 by Bruno Aristimunha)

  • braindecode.models.EMG2QwertyNet and braindecode.models.MetaNeuromotorHand give correct outputs on Intel Gaudi (HPU) in eager mode: the rotation-invariant MLP rolls a contiguous copy of its input, since Gaudi eager mode rolls a non-contiguous tensor wrongly (#1249 by Bruno Aristimunha)

  • Models now run after model.to(torch.float64), torch.bfloat16 or torch.float16, and their FFT, STFT and filter-bank front ends run on Intel Gaudi (HPU): the new braindecode.functional.spectral_input() gives these ops a float32 (at least) input, on the CPU for HPU tensors since PyTorch has no complex bfloat16 and Gaudi no complex dtype; the real result is cast back with .to(x). Used by braindecode.models.BIOT, braindecode.models.BrainBERT, braindecode.models.Brant, braindecode.models.CBraMod, braindecode.models.CodeBrain, braindecode.models.ContraWR, braindecode.models.DIVER1, braindecode.models.EEGDINO, braindecode.models.EMG2QwertyNet, braindecode.models.LUNA, braindecode.models.MAPA, braindecode.models.MetaNeuromotorHand, braindecode.models.SensingDynamics, braindecode.modules.FilterBankLayer (FBCNet, FBMSNet, FBLightConvNet, IFNet), braindecode.modules.GeneralizedGaussianFilter and braindecode.functional.hilbert_freq(). Tensors built inside forward of braindecode.models.BENDR, braindecode.models.CodeBrain, braindecode.models.DGCNN, braindecode.models.REVE, braindecode.models.SyncNet and braindecode.models.ZUNA follow the input’s dtype. CodeBrain can also train after a first forward under torch.inference_mode(). Float32 outputs and gradients are unchanged (#1246 by Bruno Aristimunha)

  • Delete each passing test’s tmp_path so the Windows CI runner no longer runs out of disk (#1251 by Bruno Aristimunha).

  • braindecode.models.BrainOmni and braindecode.models.BrainTokenizer now type CTF and KIT axial MEG gradiometers as gradiometers, as the released BrainOmni code does; they were typed as magnetometers, which gave them the wrong sensor embedding. Elekta magnetometers and planar gradiometers were already correct. (by Bruno Aristimunha)

  • Fix braindecode.models.BrainOmni.reset_head() leaving the new head in training mode after model.eval(), so its dropout made inference stochastic; the head now follows the model’s mode (by Bruno Aristimunha).

  • Fix braindecode.models.BrainTokenizer reconstruction when window_length is not a multiple of prod(ratios): each decoded window is now cropped to window_length before the windows are joined, so later windows keep their position. Configurations with a divisible window, such as the released one, are unchanged (by Bruno Aristimunha).

  • braindecode.models.BrainTokenizer and braindecode.models.BrainOmni now raise ValueError for n_filters < 2 and for attention dimensions that do not split across heads, instead of a reshape error or an assert; the tokenizer’s sampling rate warning names BrainTokenizer (by Bruno Aristimunha).

  • braindecode.models.MAPA.input_shape now returns the spectrogram shape (1, n_chans, 20, n_times) with normalization="session", so get_output_shape() works in that mode (by Bruno Aristimunha).

  • Fix from_pretrained() rejecting a caller’s chs_info (n_chans=… different from chs_info) and n_times/sfreq (n_times different from input_window_seconds * sfreq): the Hub config filled the geometry arguments the caller omitted, so values from two sources collided. The derived argument is now pinned from the caller’s one. This unblocks loading EEGPT, STEEGFormer, Brant and MVPFormer checkpoints on a montage other than their pretraining dataset’s (#1232 by Bruno Aristimunha).

  • Fix braindecode.models.CBraMod building a LazyLinear head when the geometry came from chs_info or input_window_seconds instead of n_chans / n_times; such a model could not be saved or loaded with from_pretrained (“uninitialized parameter”). The head is now a concrete Linear whenever the geometry is known (#1233 by Bruno Aristimunha).

  • braindecode.models.BrainOmni now loads the released checkpoints’ RoPE cache (cosines only) and keeps PyTorch’s default head initialisation, as the released code does. Unlike the released code, it applies no attention dropout in evaluation mode (#1244 by Bruno Aristimunha).

  • Fix braindecode.models.CBraMod failing in forward for any patch_size other than 200: the spectral reshape hard-coded 101 rFFT bins instead of patch_size // 2 + 1 (#1240 by Bruno Aristimunha).

  • Fix from_pretrained ignoring the saved geometry when called with an explicit chs_info=None or n_chans=None, and braindecode.models.Labram with neural_tokenizer=False ignoring on_non_divisible (#1250 by Bruno Aristimunha).

  • Fix braindecode.models.Deep4Net short-input auto-scaling with split_first_layer=True so the scaled filter_time_length is used by the actual braindecode.modules.CombinedConv temporal kernel instead of retaining the original constructor value. By lindicaphxag-tech.

  • Fix braindecode.models.Deep4Net with an explicit final_conv_length and no n_times. The model now skips input-length auto-scaling when the input length is intentionally unspecified, matching the documented contract that only final_conv_length="auto" requires n_times. By lindicaphxag-tech.

  • Route the attention of braindecode.models.EEGDINO and braindecode.models.Labram through their qkv linear module instead of reading its weight, so hooks and adapters on qkv (e.g. LoRA) take effect; before, they were skipped silently. Outputs change only by float rounding (#1194 by Bruno Aristimunha)

  • Fix braindecode.modules.AvgPool2dWithConv to invalidate cached pooling weights when the exact input device changes, preventing stale weights from being reused across CUDA devices or non-CUDA backends with the same dtype. By lindicaphxag-tech.

  • Fix braindecode.models.ShallowFBCSPNet with split_first_layer=False, which attempted to initialize and remap checkpoint keys through the split-only conv_time_spat module after the CombinedConv refactor. The unsplit path now initializes and loads its direct temporal convolution as before (#1212 by lindicaphxag-tech).

  • Fix braindecode.models.Deep4Net with split_first_layer=False, which attempted to initialize and remap checkpoint keys through the split-only conv_time_spat module after the CombinedConv refactor. The unsplit path now initializes and loads its direct temporal convolution as before (#1207 by lindicaphxag-tech).

  • Make braindecode.modules.TimeDistributed accept non-contiguous sequence batches by reshaping rather than requiring view-compatible strides. By lindicaphxag-tech.

  • Make braindecode.modules.Chomp1d preserve the input when chomp_size=0 instead of returning an empty time axis. This restores braindecode.models.TCN with kernel_size=1, whose causal padding is zero. By lindicaphxag-tech.

  • Preserve Deep4Net’s first-block temporal stride when using the merged CombinedConv path with stride_before_pool=True; dense-prediction outputs now retain the historical temporal geometry (#1205 by lindicaphxag-tech).

  • Fix braindecode.modules.CausalConv1d to use explicit left-only causal padding. This prevents kernel_size=1 from producing an empty time axis and keeps strided/dilated convolutions aligned with the causal reference instead of over-cropping downsampled outputs (#1216 by lindicaphxag-tech).

  • braindecode.EEGClassifier.predict_trials() and braindecode.EEGRegressor.predict_trials() no longer raise a ValueError on trials of different lengths; they return a list with one prediction array per trial (#1159 by Bruno Aristimunha)

  • Fix braindecode.models.EEGMiner on Intel Gaudi (HPU), part 2 of #1183: braindecode.modules.GeneralizedGaussianFilter and braindecode.functional.hilbert_freq() now use a real-valued DFT on devices without complex tensors, computed in float32 with autocast disabled (a bf16 matmul DFT gave phase-locking features 20% off and wrong-direction filter gradients). CPU and CUDA keep torch.fft (#1193 by Bruno Aristimunha)

  • Compute the 4-D Fourier position embedding of braindecode.models.REVE in float32 with autocast disabled. Intel Gaudi (HPU) autocast downcasts the position × frequency products to bf16 before sin/cos (embedding ~3 % off on Gaudi2); CPU/CUDA results are unchanged (#1192 by Bruno Aristimunha)

  • Fix braindecode.models.MVPFormer on Intel Gaudi (HPU): the channel-relative shift is now a single torch.gather and the grouped-query key repeat works on a contiguous copy. On HPU the previous advanced indexing back-propagated through a host-side index_put_ with wrong gradients (and took 108 s per 30 s-window training step), and the repeat scrambled the relative keys (features 14-39 % off). CPU and CUDA results are bit-identical (#1189 by Bruno Aristimunha)

  • Fix the positional encoder of braindecode.models.SignalJEPA and braindecode.models.SignalJEPA_Contextual on Intel Gaudi (HPU): the time table is now a non-persistent buffer that follows .to(device), and the encoding is built with torch.cat instead of strided in-place writes (the temporal part was 73 % off on Gaudi2). State-dict keys and CPU/CUDA outputs are unchanged (#1191 by Bruno Aristimunha)

  • Fix braindecode.EEGRegressor training on datasets with one target per trial: the (batch,) target is now reshaped to match a (batch, 1) prediction instead of being broadcast to (batch, batch) by the loss, and braindecode.EEGRegressor.fit() now returns self (#1180 by Arthur031221).

  • Preserve shared class targets when creating MNE epochs from different event annotations, as in sleep staging. MNE event IDs remain unique. (#1174 by Bruno Aristimunha)

  • Fix a FutureWarning raised by MNE >= 1.13 when importing braindecode or using models, datasets and augmentations that build on the standard_1005/standard_1020 montages: MNE renamed these montages to colin27_1005/colin27_1020 and will remove the legacy names in MNE 1.14, so their spelling is now resolved against the installed MNE version via braindecode.util.resolve_montage_name() (#1163 by Li Qing).

  • Fix braindecode.modules.CombinedConv raising RuntimeError: self must be a matrix when in_chans, n_filters_time or n_filters_spat is 1, which made braindecode.models.ShallowFBCSPNet and braindecode.models.Deep4Net unusable on single-channel data (#1154 by Julien Gadonneix).

  • Fix braindecode.models.base.EEGModuleMixin.reset_head() leaving the saved configuration on the previous head, so a model saved after changing its number of outputs could not be loaded back. Eighteen models (BENDR, BIOT, CBraMod, EEGDINO, EEGPT, Labram, MetaNeuromotorHand, MVPFormer, REVE, STEEGFormer, ZUNA, the three SignalJEPA classifiers and the Interpolated BENDR, BIOT, EEGPT and LaBraM wrappers) now record the new n_outputs, and BENDR, CBraMod and EEGDINO built as feature extractors now also record that they became classifiers, instead of reloading without their trained head. Existing head-reset train/eval behavior is unchanged (#1181 by Raghav Rathi).

  • Make braindecode.preprocessing.create_windows_from_events() infer the event mapping once for the whole dataset before the recordings are windowed. With mapping=None and n_jobs above one, every worker numbered the event descriptions of its own recording from zero, so the same description could receive different integer targets across recordings. By Sarthak Tayal.

  • Make braindecode.datautil.load_concat_dataset() restore the targets_from and last_target_only settings of a saved braindecode.datasets.EEGWindowsDataset. The loader looked the stored settings up under the name WindowsDataset while the windowers record them under EEGWindowsDataset, so a dataset windowed with targets_from="channels" came back reading its targets from the metadata. By Sarthak Tayal.

  • Make braindecode.datasets.BaseConcatDataset.get_metadata() work on a copy of the metadata of each dataset. The description columns were written into the metadata frame of the dataset itself, replacing any column sharing a name with a description key such as target. By Sarthak Tayal.

  • Make braindecode.preprocessing.create_windows_from_events() accept a mapping that sends several event descriptions to the same target when the windows are stored as mne.Epochs, for example to merge sleep stages 3 and 4. mne.Epochs rejects an event_id with repeated values since MNE 1.13, so annotations now receive distinct event IDs while their shared targets remain in the window metadata. By Sarthak Tayal.

  • Fix braindecode.models.STEEGFormer on high-density sensor nets whose electrodes are numbered rather than named for a 10-20 site (e.g. EGI HydroCel E1 … E256). A channel name outside the montage vocabulary used to switch the whole montage to the identity mapping, which is meaningless and raised chan_pos_idx values must be in [0, 145) above 145 channels. Such channels now take the slot of the nearest 10-05 site from their chs_info position, while named channels keep their own slot. Without positions, the identity fallback is kept, and a clear error is raised when it cannot fit (#1185 by Bruno Aristimunha).

  • Fix braindecode.models.EEGMiner on Intel Gaudi (HPU) and under torch.jit.trace: braindecode.modules.GeneralizedGaussianFilter now clamps its parameters in place under torch.no_grad() instead of reassigning .data, and braindecode.functional.hilbert_freq() computes the complex FFT step in float32 for bfloat16 inputs, so the phase features work under bfloat16 autocast (#1183 by Bruno Aristimunha).

  • Fix braindecode.modules.MaxNormParametrize failing on Intel Gaudi (HPU), which affects every model with a max-norm weight constraint (e.g. braindecode.models.EEGNet, braindecode.models.ATCNet). The row rescale is now written out instead of calling Tensor.renorm; values and gradients are unchanged (#1184 by Bruno Aristimunha).

  • Fix braindecode.functional.hilbert_freq() with forward_fourier=True returning one sample fewer than the input for odd-length signals and doubling the Nyquist coefficient for even-length ones. It now matches scipy.signal.hilbert() for both. braindecode.functional.plv_time() on time-domain input uses the corrected transform (#1188 by Arthur031221).

  • Fix braindecode.modules.MaxNormParametrize producing NaN outputs or gradients for zero or very small float16 rows after #1184; the norm and scale are computed in float32 for float16/bfloat16 inputs, while float64 precision is preserved. Safe denominators prevent invalid intermediate gradients, and rows at or below max_norm are unchanged. Empty tensors pass through and a negative max_norm raises, as Tensor.renorm does (#1187 by Bruno Aristimunha).

  • Keep the channel IDs of braindecode.models.EEGPT (chans_id) out of the state dict. They are rebuilt from chs_info, and a checkpoint that still stores them no longer overrides them: the released weights now load on any montage and with the default channel projection, where they failed with a size mismatch, and a montage with the same channel count no longer silently takes the checkpoint’s IDs (#1195 by Bruno Aristimunha).

  • Fix braindecode.models.Labram so the released weights keep their pretrained time embedding at every window length: it now holds the original 16 absolute time slots (patch p uses slot p) instead of one slot per patch plus one, which matched the released weights only for 15-patch windows. Checkpoints saved with the previous layout load with identical outputs, and windows longer than 16 patches warn that their extra slots keep their initialization (#1155 by Bruno Aristimunha).

  • Fix cropped braindecode.EEGRegressor training on a 1-D numpy y computing its loss on a (batch_size, batch_size) broadcast. braindecode.EEGRegressor.fit() reshapes such a y to (n_trials, 1), and braindecode.training.CroppedLoss squeezed the time-averaged prediction to (batch_size,). It now keeps the output dimension when the target is 2-D (#1198 by Raghav Rathi).

  • Fix channel resolution in the channel layer: T3 and T7 stay distinct; a misspelt strategy raises with the closest name; coordinate-only channels match a target within 15 mm; non-EEG channels raise (or are dropped with drop_non_eeg=True); legacy names are copies; spline is regularised (reg=1e-3) and a row gain above 2 warns; fewer than four positioned channels raise a ValueError (#1241 by Bruno Aristimunha).

Current 1.8.0 (2026-08-31)#

Enhancements#

API and behavior changes#

  • Deprecate drop_last_window in braindecode.preprocessing.create_windows_from_events() and braindecode.preprocessing.create_fixed_length_windows(); it will be removed in version 2.0. The keyword-only replacement, on_last_window, accepts 'overlap' or 'drop'. Explicit legacy False maps to 'overlap' and explicit legacy True maps to 'drop'; either legacy spelling emits a DeprecationWarning. When no explicit window size or stride is supplied to fixed-length windowing, the policy is immaterial and normalizes to the existing single full-recording window; this also replaces the internal assertion formerly reached by explicit legacy True. (#1058 by Michele Romani)

Requirements#

Bug fixes#

Code health#

  • None yet

Current 1.7.0 (2026-08-01)#

Enhancements#

  • Add braindecode.models.ZUNA, a position-aware EEG foundation model from Warner et al. (2026), ported from the public Zyphra/ZUNA1.1 encoder with a Braindecode classification head, shared patch tokenization, and construction-time spatial positions compatible with TorchScript and torch.compile (#1020 by Jon Huml)

  • Add braindecode.models.util.interpolated_models_dict, a dedicated registry for the interpolated (channel-adapting) models, keeping them separate from braindecode.models.util.models_dict (#1093 by Bruno Aristimunha)

API and behavior changes#

  • The stride_factor parameter of braindecode.models.FBLightConvNet is deprecated and will be removed in a future release. The model never read it, its temporal segmentation is set by win_len, and the reference implementation has no such parameter. Passing it now emits a DeprecationWarning and keeps being ignored. By Sarthak Tayal.

  • Interpolated models (e.g. InterpolatedBIOT, InterpolatedLaBraM) are no longer included in braindecode.models.util.models_dict; they now live in the separate braindecode.models.util.interpolated_models_dict registry. They remain fully usable and resolvable by name in the skorch wrappers and pydantic configs (#1093 by Bruno Aristimunha)

Requirements#

  • None yet

Bug fixes#

  • Fix braindecode.EEGClassifier.predict_trials(), braindecode.EEGRegressor.predict_trials() and braindecode.training.scoring.CroppedTrialEpochScoring on the training set raising AttributeError or TypeError when the module was passed as a model name or as an uninstantiated class. Those paths read the module constructor argument instead of the initialized module_ attribute, so they only worked when an already instantiated module was passed, which the documentation discourages. By Sarthak Tayal.

  • Raise a clear ValueError in braindecode.models.FBLightConvNet when n_times is shorter than win_len. The temporal attention kernel is sized as n_times // win_len, so a short window produced an empty kernel and the constructor died with a ZeroDivisionError coming from the weight initialisation. The docstring now also documents win_len and reports the correct n_bands default of 9. By Sarthak Tayal.

  • Use nn.Dropout1d instead of nn.Dropout2d for the channel-wise dropout inside braindecode.models.BDTCN, braindecode.models.TCN and braindecode.models.BENDR. Those layers receive (batch, channels, times) activations, which nn.Dropout2d only handles through a deprecated fallback that warns on every forward pass and is scheduled to be reinterpreted as unbatched input, masking batch items rather than channels. The masking behaviour is unchanged. By Sarthak Tayal.

  • Clarify that braindecode.training.scoring.predict_trials(), braindecode.EEGClassifier.predict_trials(), and braindecode.EEGRegressor.predict_trials() return ground-truth dataset targets alongside predictions. By Sarthak Tayal.

  • Make braindecode.models.EEGMiner compatible with TorchScript across magnitude, correlation, and phase-locking-value feature modes by replacing runtime callable dispatch with a scriptable feature module. (#1101 by Sarthak Tayal)

  • Make the braindecode.models.REVE position bank robust on offline / limited-network nodes: it is now cached in the writable MNE data directory (resolved via the REVE_POSITIONS_PATH config key, defaulting under ~/mne_data) instead of the package folder, so a prefetched reve_positions.json there is used without any download (#1098 by Bruno Aristimunha)

Code health#

  • None yet

Current 1.6.1 (2026-07-01)#

Enhancements#

  • Pin the %pip install braindecode cell in generated notebooks to the version used to build them (braindecode==X.Y.Z for stable releases, git+https://github.com/braindecode/braindecode.git for dev builds), so notebooks can be reproduced with a matching installation. (#1080 by Fashad Ahmed)

  • Add braindecode.functional.sinusoidal_positional_encoding(), a shared sine/cosine positional-encoding primitive (handling odd dimensions), and reuse it in braindecode.models.BIOT, braindecode.models.MEDFormer, and braindecode.models.STEEGFormer instead of re-deriving the table in each. Encodings are bit-identical, so model behavior is unchanged. Also add braindecode.modules.GatedLinearUnit, a configurable GLU-family gate (GEGLU with the default nn.GELU, SwiGLU with nn.SiLU; torch.nn.GLU is hard-wired to the sigmoid) for building gated transformer feed-forwards. (#1078 by Bruno Aristimunha)

  • Add braindecode.models.DANCE, an event detection-and-classification model (CNN encoder + Perceiver bottleneck + DETR-style decoder) that detects a set of (start, end, class) events from long, unaligned EEG windows, with a braindecode.training.DanceLoss criterion, an f1_event detection metric, and a runnable tutorial. The re-implemented spatial merger / Perceiver / conv stack are numerically parity-verified against the upstream reference. (#1075 by Bruno Aristimunha)

  • Add an optional spatial Fourier braindecode.modules.ChannelMerger (with braindecode.modules.FourierEmb) to braindecode.models.BrainModule via use_merger=True, implementing the montage-agnostic spatial attention its docstring described. Also fixes a latent crash for non-integer dilation_growth and sizes the channel accounting in forward order so subject_layers/STFT combinations work. Default-off, so existing behavior is unchanged. (#1076 by Bruno Aristimunha)

  • Add opt-in electrode positions in the batch via braindecode.datasets.BaseConcatDataset.set_return_ch_pos() and a cached ch_pos accessor on windowed datasets, plus braindecode.datasets.pad_channels_collate() to make collections with heterogeneous montages (different channel sets) batchable. Positions and a channel mask are routed into the model’s forward under braindecode.EEGClassifier. Default-off, so the (X, y, crop_inds) contract is unchanged. (#1066 by Bruno Aristimunha)

  • Add a revision keyword argument to braindecode.datasets.BaseConcatDataset.pull_from_hub() so callers can pin dataset downloads to a specific branch, tag, or commit on the Hugging Face Hub.

  • Clarify the model summary table’s Type column by using Prediction for supervised heads instead of describing them as classification-only. By Sarthak Tayal.

  • Add a Modality column (EEG, MEG, sEMG, …) to the model summary table so models can be filtered by the bio-signal they target, and validate it against a controlled vocabulary. By Bhargav Kowshik.

  • Add braindecode.models.InterpolatedEEGPT, a channel-interpolation variant of braindecode.models.EEGPT built with InterpolatedModel(). By Pierre Guetschel.

  • Add braindecode.models.EEGDINO, the EEG-DINO self-distillation foundation model (Small/Medium/Large) with pretrained S/M weights. By Bruno Aristimunha.

  • Add braindecode.models.MVPFormer, the multi-variate parallel attention (MVPA) foundation model for heterogeneous multi-variate iEEG, with a db4-wavelet signal encoder computed from first principles (no new dependency). By Bruno Aristimunha.

  • Add braindecode.models.STEEGFormer, a ViT-based EEG foundation model pre-trained with a masked-autoencoder objective, from Yang et al. (ICLR 2026). By Adam Mounir.

  • Add separate EEGNet and TCN dropout rates to braindecode.models.EEGTCNet via the new drop_prob_eeg and drop_prob_tcn arguments, enabling the source/paper configuration (p_eeg=0.2, p_tcn=0.3). Both default to drop_prob, so existing behavior is unchanged. (#1060 by Bruno Aristimunha)

  • Add an opt-in conv_max_norm_const argument and a source_optimizer_param_groups() helper to braindecode.models.ATCNet, exposing the official implementation’s convolution/TCN max-norm constraint (0.6) and L2 weight-decay groups (conv/TCN 0.009, dense 0.5). Defaults preserve current behavior. (#1061 by Bruno Aristimunha)

  • Add causal forward filtering support to braindecode.modules.FilterBankLayer for IIR and FIR filter banks via phase="forward"/phase="causal". Existing zero-phase filtering remains the default. By Bruno Aristimunha.

  • Add braindecode.models.TCFormer, the Temporal Convolutional Transformer for EEG motor-imagery decoding: a multi-kernel CNN front-end, a grouped-query attention Transformer with rotary positional embeddings, and a grouped temporal convolutional network head. (#1065 by Bruno Aristimunha)

  • Add a return_features option to braindecode.models.FBMSNet: when enabled, forward() returns (logits, features) where features is the flattened pre-classifier vector (shape (batch, out_channels_spatial * stride_factor)), enabling center-loss training without subclassing. (#1083 by Bruno Aristimunha)

  • Add an n_augmentation argument to braindecode.augmentation.AugmentedDataLoader for fixed set-expansion: each batch keeps its clean originals and appends n_augmentation independently transformed copies (e.g. the EEG-Inception 6x training set with n_augmentation=5); the default 0 preserves the current in-place behavior. The collate is now a picklable callable, so the loader supports num_workers > 0, and several augmentation transforms were vectorized for speed. (#1070 by Bruno Aristimunha)

API and behavior changes#

Requirements#

  • Cap the test dependency to pytest<9.1 in the tests extra. pytest 9.1.0 changed the IdMaker constructor signature, which breaks pytest_cases 3.10.1 and makes the whole test suite crash at collection time. The cap can be lifted once pytest_cases supports pytest>=9.1. By Adam Mounir.

Bug fixes#

Code health#

  • Silence the new “training set smaller than batch_size” warning (#1053) in the test_eegneuralnet signal-argument tests, which intentionally fit tiny mock data to check argument propagation rather than to train. Keeps the warning meaningful by not emitting it on every CI run. (#1056 by Adam Mounir)

  • Install CPU-only PyTorch wheels in the tests and docs CI workflows via UV_TORCH_BACKEND=cpu. GitHub runners have no GPU, so the default CUDA build pulled ~1.8 GiB of unused nvidia-* wheels and contributed to a disk-exhaustion crash. (#1054 by Bhargav Kowshik)

  • Add a monthly scheduled workflow that cuts a stable PyPI release on the 1st of every month, complementing the existing per-push .devN pipeline. (#1030 by Bruno Aristimunha)

  • Make the monthly release workflow push only the release tag and open a pull request for the version bump, so it never needs to push to the protected master branch. (#1031 by Bruno Aristimunha)

Current 1.5.1 (stable)#

Enhancements#

  • Add braindecode.augmentation.BandRotation and braindecode.augmentation.functional.band_rotation(): per-band circular roll along the channel axis plus inter-band temporal jitter, for surface-EMG inputs shaped (B, num_bands * electrodes_per_band, T). Models small wristband rotation between sessions and relative timing noise between two arms, from the emg2qwerty paper (Sivakumar et al., NeurIPS 2024). (#1013 by Bruno Aristimunha)

  • Build SpecAugment (Park et al., Interspeech 2019) into braindecode.models.EMG2QwertyNet as a parameter-free submodule gated by a new spec_augment constructor flag (default False). When enabled, applies up to n_time_masks × time_mask_param time bands and n_freq_masks × freq_mask_param frequency bands on the log-spectrogram during train() only, with masks sampled IID per (sample × band × electrode) triple — same recipe as the upstream emg2qwerty.transforms.SpecAugment dataset transform. The mask fill value stays a 0-D on-device tensor (spec.mean()) so the forward pass adds no host round-trip on GPU. Also adds a return_features runtime flag to braindecode.models.EMG2QwertyNet.forward() (returns {"features": (B, T_out, num_features), "cls_token": None}, BIOT / signal-JEPA convention) and a matching return_feature constructor flag (returns (emissions, features) tuple, BIOT-style legacy path) so downstream wrappers — such as neuroai’s DownstreamWrapperModel — can pick up the encoder representation via model_output_key="features" (dict) or model_output_key=1 (tuple) without changes to their call site. (#1015 by Bruno Aristimunha)

API and behavior changes#

  • Restore the per-batch ch_names keyword argument on braindecode.models.Labram.forward() that was removed in 1.5.0 (#993). ch_names is now keyword-only and, when provided, case-insensitively matches each name to braindecode.models.labram.LABRAM_CHANNEL_ORDER and indexes into the canonical position-embedding bank, so callers can forward an arbitrary subset of canonical channels without going through braindecode.models.InterpolatedLaBraM. The strict ValueError raised in 1.5.0 when chs_info did not match LABRAM_CHANNEL_ORDER is downgraded to a UserWarning so that downstream wrappers (e.g. neuroai’s _LabramChannelWrapper) can build the inner braindecode.models.Labram with their union channel set and resolve the subset per batch via ch_names.

Bug fixes#

Current 1.5.0 (stable)#

Enhancements#

  • Add braindecode.datasets.BaseConcatDataset.set_target() to swap any per-window metadata column or per-record description field (e.g. a BIDS entity, a participants.tsv extra) into the dataset’s target y in one call, replacing the manual for ds in concat.datasets: ds.metadata.loc[:, 'target'] = ...; ds.y = ... loop. Dispatches on the subdataset type: writes metadata['target'] / ds.y for windowed records, and points target_name at the chosen description field for raw records. By Bruno Aristimunha.

  • Redesign the documentation landing page (docs/index.rst) in a pyhealth.dev-style layout: animated brain → EEG → net hero, fact strip highlighting MOABB / EEGDash interoperability, interactive model-zoo browser sourced from braindecode/models/summary.csv, Hugging Face Hub integration row, and a tutorial-thumbnail carousel. Adds sphinxext-opengraph to docs extras and a SoftwareApplication JSON-LD block. (#1007 by Bruno Aristimunha)

  • Tutorials now train for a few epochs then load pretrained weights from Hugging Face Hub to show full training curves and metrics. All 9 tutorial checkpoints published to huggingface.co/braindecode/. The offline training script used to produce the checkpoints is available as a gist: https://gist.github.com/bruAristimunha/27d74c8410fe9d0db258a03f42efa7c6. (#985 by Bruno Aristimunha)

  • Use F.scaled_dot_product_attention in braindecode.modules.MultiHeadAttention, enabling optimized attention kernels (flash-attention on CUDA, memory-efficient backends on other devices). By Léo Burgund and Bruno Aristimunha. (#902)

  • Add experimental channel interpolation feature: new braindecode.modules.ChannelInterpolationLayer plus the braindecode.models.InterpolatedModel() class factory project arbitrary user channel sets to a model’s canonical set via an MNE-backed (frozen by default) interpolation matrix. Ship pre-built variants braindecode.models.InterpolatedLaBraM, braindecode.models.InterpolatedSignalJEPA, and braindecode.models.InterpolatedBIOT for the corresponding pre-trained models. (#993 by Pierre Guetschel)

  • Add a tutorial walking through the experimental Interpolated* family (Running a Pretrained Model on Any Channel Set with the Channel Layer): failure mode of the vanilla backbones on non-canonical channel sets, one-line fix via braindecode.models.InterpolatedLaBraM / braindecode.models.InterpolatedBIOT / braindecode.models.InterpolatedSignalJEPA, side-by-side visualisation of the name_match vs always interpolation matrices, and the trainable=True flag. (#994 by Pierre Guetschel)

  • Mark deterministic index buffers (braindecode.models.BIOT encoder’s index and braindecode.models.REVE’s position embedding bank) as non-persistent. They are rebuilt from __init__ arguments on every instantiation, so keeping them in state_dict only bloated checkpoints and caused spurious mismatches when n_chans (or the position-bank config) differed between save and load. (#993 by Pierre Guetschel)

  • Add braindecode.visualization interpretability utilities, all built on plain PyTorch autograd with no extra dependencies: saliency(), input_x_gradient(), integrated_gradients(), layer_grad_cam(), project_to_topomap() (thin wrapper around mne.viz.plot_topomap()), and compute_metrics() for quantitative attribution comparison. A new tutorial, Interpretability of EEG Decoders, walks through the full pipeline.

  • Add braindecode.models.MetaNeuromotorHand, a port of the handwriting decoder from Meta / CTRL-labs’ generic neuromotor interface (Kaifosh, Reardon et al., Nature 2025). The model takes raw 16-channel surface EMG from the wristband at 2 kHz and produces per-token scores for CTC decoding of handwritten text. The pipeline is a fixed multivariate power frequency (MPF) featurizer (channel-wise STFT, cross-spectral density, frequency-band averaging, and SPD matrix logarithm) followed by a circular rotation-invariant MLP and a 15-block causal conformer encoder. Meta’s pretrained checkpoint loads directly via load_state_dict (after stripping the network. prefix); the port is bit-exact to the upstream reference implementation on real sEMG. Distributed under CC BY-NC 4.0 to match the upstream repository; see the class docstring for the license warning and the pretrained-checkpoint loading recipe. By Bruno Aristimunha.

  • Add braindecode.models.EMG2QwertyNet, a port of the TDS-Conv-CTC touch-typing decoder from facebookresearch/emg2qwerty (Sivakumar et al., NeurIPS 2024). The model takes raw 32-channel surface EMG (two 16-electrode wristbands at 2 kHz) and emits per-frame scores over a 99-class typing vocabulary (98 keys + CTC blank); pass log_softmax=True to get log-probabilities directly consumable by CTCLoss. The pipeline is a parameter-free log-spectrogram front-end, per-electrode-per-band BatchNorm, a circular rotation-invariant MLP (one per band), and a stack of Time-Depth-Separable convolutional blocks (Hannun et al., 2019) without temporal padding. The encoder nn.Sequential mirrors upstream’s TDSConvCTCModule.model indices for parameter-bearing children, so upstream emg2qwerty checkpoints load directly via load_state_dict (after stripping the PyTorch-Lightning network. prefix); the class-level mapping only remaps the head from model.4.{weight,bias} to final_layer.{weight,bias}. Distributed under CC BY-NC 4.0 to match the upstream repository; see the class docstring for the license warning and the pretrained-checkpoint loading recipe. By Bruno Aristimunha.

API and behavior changes#

  • braindecode.modules.MultiHeadAttention now follows PyTorch’s SDPA mask convention: boolean masks use True to ignore a position (previously True meant keep). The scaling factor is now 1/sqrt(head_dim) instead of 1/sqrt(emb_size). (#902)

  • braindecode.models.BENDR: remove the n_chans_pretrained / chan_proj_max_norm parameters and the channel_projection layer; hard-code the 20 pre-training channels as _BENDR_TARGET_CHS_TUPLES. The official braindecode/braindecode-bendr checkpoint has been re-uploaded flat so from_pretrained now loads its 99 weights (previously 0 of 99 matched silently). Also ships braindecode.models.InterpolatedBENDR, the InterpolatedModel() wrapper that accepts arbitrary user chs_info and projects to the canonical 20 BENDR channels (the SCALE target has no physical position, so its interpolation row is a spatial spline of the user’s EEG — not the dn3 amplitude statistic). (#992 by Pierre Guetschel)

  • braindecode.models.Labram now requires chs_info to match LABRAM_CHANNEL_ORDER exactly (128 channels, canonical order). The on_unknown_chs parameter and the forward-time ch_names argument are removed. Users with arbitrary channel sets should migrate to braindecode.models.InterpolatedLaBraM. (#993 by Pierre Guetschel)

Bug fixes#

  • Fix braindecode.models.SyncNet swapped parameter initialization where phi_ini (phase shift) was using beta_init_values and beta (decay) was using phase_init_values, replaced incorrect .view() reshape with .permute() for proper conv2d filter weight layout, and fixed duplicate default values in docstring (by Sarthak Tayal)

  • Fix braindecode.models.AttentionBaseNet redundant super().__init__() call that ran the parent nn.Module.__init__ twice (by Sarthak Tayal)

  • Fix incomplete author email in braindecode.models.TSception header (by Sarthak Tayal)

  • Fix a time-of-check-time-of-use race in braindecode.datasets.base._zarr_to_memmap() that caused concurrent workers to repeatedly rename-replace the published .npy cache, producing wasted I/O on local filesystems and .nfsXXXX silly-rename files plus SIGBUS crashes on NFSv3. The published file is now created exactly once via os.link and is never replaced, making the cache safe under arbitrary concurrent access on local POSIX, NFSv3, Lustre and SMB (#986 by Pierre Guetschel)

  • Register braindecode.models.BIOT encoder index as a non-trainable buffer instead of a parameter (torch.long), so it is treated as module state rather than trainable weights (#988 by Pierre Guetschel)

  • Fix TypeError: type 'Any' is not subscriptable when importing braindecode.models.config without numpydantic installed on Python 3.12+ (#871 by Sarthak Tayal)

  • Fix braindecode.preprocessing.create_fixed_length_windows() crashing when only window_size_samples is provided without window_stride_samples, stride now defaults to window size as documented (#990 by Sarthak Tayal)

  • Add channel_embedding parameter to braindecode.models.SignalJEPA and braindecode.models.SignalJEPA_Contextual to load pre-trained channel embedding weights when fine-tuning on a subset of the pre-training channels. Two new HuggingFace checkpoints are published: braindecode/signal-jepa and braindecode/signal-jepa_without-chans (#991 by Pierre Guetschel)

  • Bump openneuro-py to >=2026.4.0 so the docs build picks up upstream PR #308 (DatasetFile.key → id). The previous <2026.4 pin (#1000) avoided a libc double-free seen with newer releases but broke examples/datasets_io/plot_bids_dataset_example.py against the live OpenNeuro 5.0.0 GraphQL schema (Cannot query field "key" on type "DatasetFile") (#1002 by Bruno Aristimunha)

  • Retry transient TLS failures from physionet.org when fetching braindecode.datasets.SleepPhysionet (by Bruno Aristimunha)

Current 1.4.0 (stable)#

Enhancements#

API changes#

  • Add braindecode.models.base.EEGModuleMixin.get_config() and braindecode.models.base.EEGModuleMixin.from_config() to all models, enabling full JSON round-trip serialization and reconstruction of any model including all __init__ parameters (by Bruno Aristimunha)

  • push_to_hub() now saves all model parameters to config.json (previously only 6 EEG-specific parameters were saved; model-specific parameters like F1, D, drop_prob were lost on reload) (by Bruno Aristimunha)

  • Add braindecode.modules.Square activation module and update braindecode.models.ShallowFBCSPNet to use type[nn.Module] for conv_nonlin (backward-compatible with callable) (by Bruno Aristimunha)

  • Replace LazyLinear with Linear in braindecode.models.CBraMod when input dimensions are known, improving Hub round-trip compatibility (by Bruno Aristimunha)

Requirements#

  • Relaxed PyTorch requirement to >=2.0 to support Intel-based Macs (by GalAshkenazi1)

Bugs#

  • Improve the error message when from_pretrained() or push_to_hub() are called without the optional huggingface_hub dependency installed. Users now get a clear ImportError with installation instructions (pip install 'braindecode[hub]') instead of an AttributeError. (#1024 by @copilot)

  • Replace the vague license: unknown field in the auto-generated Hugging Face Hub dataset card with an inferred license (when consistent across all recordings’ descriptions) or a please-specify placeholder with a reminder comment for the user to fill in the dataset’s license. (#1014 by Fashad Ahmed)

  • Fix the documentation header “Cite Braindecode” announcement link: it used a bare cite.html URL, which browsers resolve relative to the current page path and led to 404s (for example from install/install.html). The link is now built with Sphinx’s pathto() for each page so it always targets the cite page correctly.

  • Fix braindecode.models.EEGITNet state dict mapping that pointed bias to the weight key and referenced a nonexistent submodule path, and fix third inception branch using the wrong variable for kernel length (by Sarthak Tayal)

  • Fix braindecode.models.EEGInceptionMI state dict mapping typo where the old key was tc.bias instead of fc.bias (by Sarthak Tayal)

  • Fix multi-target channel windowing in braindecode.preprocessing.windowers.create_windows_from_target_channels() to use the union of valid target positions across all misc channels instead of only the first channel (by Sarthak Tayal)

  • Fix braindecode.preprocessing.preprocess.filterbank() to preserve info fields (description, line_freq, device_info, etc.) when creating filtered copies, avoiding merge conflicts in MNE when adding channels (#928 by Bruno Aristimunha)

  • [Outdated:] Restrict to ``pandas>=3.0`` due to incompatibility with ``wfdb`` (#919 by Pierre Guetschel)

  • Fix multiple bugs in Labram positional encoding. Now the braindecode implementation is aligned with the original one (#931 by Pierre Guetschel )

  • Fix Zenodo citation: update to global concept DOI and add BibTeX/APA citation formats in docs/cite.rst, README.rst, CITATION.cff, and docs/conf.py (#937 by Bruno Aristimunha)

  • Fix channel reduction in braindecode.modules.SqueezeAndExcitation to avoid runtime shape mismatches when the reduced channel count differs from the reduction rate (#889 by Sarthak Tayal)

  • Push large datasets to HuggingFace Hub using huggingface_hub.upload_large_folder() to avoid limitations, and allow resuming downloads (#945 and #953 by Pierre Guetschel)

  • Fix braindecode.models.LUNA channel location embeddings repeated along batch dimension instead of patch dimension in prepare_tokens, and include pretrained weight typo mapping in self.mapping (#887 by Sarthak Tayal)

  • Fix temporal generalization tutorial producing degraded results (peak AUC dropped from ~0.9 to ~0.75): MEG data in SI units (T/m) has variances ~1e-23, so BatchNorm1d’s eps=1e-5 dominated the normalization denominator. Now uses epochs.get_data(units="fT/cm") to bring data to a reasonable scale, and removes the misleading “importance of normalization” section whose conclusions were an artifact of the data scale issue (by Bruno Aristimunha)

  • Fix braindecode.augmentation.BandstopFilter notch center frequency range using bandwidth/2 instead of 2*bandwidth to match docstring (#548 by Sarthak Tayal)

  • Fix braindecode.models.DeepSleepNet hardcoded linear layer size that caused a shape mismatch when using input shapes other than the default 1 channel, 3000 timepoints. The FC and BiLSTM input dimensions are now computed dynamically from the CNN output (#755 by Sarthak Tayal)

  • Fix model docstring inheritance: track_model_init_kwargs wrapped __init__ with @wraps before the NumpyDocstringInheritanceInitMeta metaclass ran, causing inspect.unwrap() to bypass the wrapper and read __doc__=None. This replaced every model’s description with the parent mixin’s and marked all model-specific parameters as “The description is missing” when DOCSTRING_INHERITANCE_ENABLE=1 was set during documentation builds (#971 by Bruno Aristimunha)

  • Expose max_nbytes directly on braindecode.preprocessing.preprocess.preprocess() and turn the cryptic mmap can't resize a readonly failure (raised when joblib memory-maps a preloaded array that a preprocessor later tries to resize) into an actionable error explaining how to fix it: pass max_nbytes=None to disable memory mapping, or supply a save_dir so data is reloaded with preload=False (#325 by Sarthak Tayal)

Code health#

  • Reorder model categories in documentation to follow the progression: Convolution, Filterbank, Interpretability, Recurrent, Attention/Transformer, SPD, Graph Neural Network, Channel, and Foundation Model (#962 by Bruno Aristimunha)

  • Fix documentation build warnings and errors: correct numpydoc section underlines in braindecode.models.EEGSym and braindecode.models.SSTDPN, strip upstream .. rubric:: directives from MNE and MOABB docstrings that caused Sphinx errors, fix RST title levels in whats_new.rst, correct bibtex key for EEGPT, and ensure conf.py prioritises the local package on sys.path (by Bruno Aristimunha)

  • Remove deprecated torch.irfft fallback in braindecode.visualization.gradients.compute_amplitude_gradients_for_X(), now uses torch.fft.irfft directly since braindecode requires torch>=2.2 (by Sarthak Tayal)

Current 1.3.2 (stable)#

Enhancements#

API changes#

  • BIDS and Hub modules moved to braindecode.datasets.bids subpackage: braindecode.datasets.bids.hub, braindecode.datasets.bids.hub_format, braindecode.datasets.bids.datasets, braindecode.datasets.bids.hub_validation (#871 by Bruno Aristimunha)

  • Deprecating the old naming of MOABB Dataset name (#826 by Bruno Aristimunha)

  • Exposing the braindecode.datautil.infer_signal_properties() utility function (#856 by Pierre Guetschel)

  • Deprecating the old naming of MOABB Dataset name #826 by Bruno Aristimunha

  • Drop support for Python 3.10 and increase support to Python 3.13 and python 3.14 (#840 by Bruno Aristimunha)

    • Model config helpers now soft-import pydantic/numpydantic; if the optional dependencies are missing the module skips config generation and warns to install pip install braindecode[pydantic].

Bugs#

Current 1.2#

Enhancements#

API changes#

  • Using the name from the original name and deprecation models that we create for no reason, models #775 by Bruno Aristimunha

  • Deprecated the version name in braindecode.models.EEGNetv4 in favour of braindecode.models.EEGNetv.

  • Deprecated the version name in braindecode.models.SleepStagerEldele2021 in favour of braindecode.models.AttnSleep.

  • Deprecated the version name in braindecode.models.TSceptionV1 in favour of braindecode.models.TSception.

Version 1.1.1#

Enhancements#

  • Massive refactor of the model webpage

Bugs#

Version 1.0#

Enhancements#

Bugs#

API changes#

Version 0.8 (11-2022)#

Enhancements#

Bugs#

API changes#

Version 0.7 (10-2022)#

Enhancements#

Bugs#

API changes#

  • Renaming the method get_params to get_augmentation_params in augmentation classes. This makes the Transform module compatible with scikit-learn cloning mechanism (#388 by Bruno Aristimunha and Alex Gramfort)

  • Delaying the deprecation of the preprocessing scale function braindecode.preprocessing.scale() and updates tutorials where the function were used. (#413 by Bruno Aristimunha)

  • Removing deprecated functions and classes braindecode.preprocessing.zscore(), braindecode.datautil.MNEPreproc and braindecode.datautil.NumpyPreproc (#415 by Bruno Aristimunha)

  • Setting iterator_train__drop_last=True by default for braindecode.EEGClassifier and braindecode.EEGRegressor (#411 by Robin Tibor Schirrmeister)

Version 0.6 (2021-12-06)#

Enhancements#

Bugs#

API changes#

Version 0.5.1 (2021-07-14)#

Enhancements#

Bugs#

API changes#

  • Preprocessor classes braindecode.datautil.MNEPreproc and braindecode.datautil.NumpyPreproc are deprecated in favor of braindecode.datautil.Preprocessor (#197 by Hubert Banville)

  • Parameter stop_offset_samples of braindecode.datautil.create_fixed_length_windows() must now be set to None instead of 0 to indicate the end of the recording (#152 by Hubert Banville)

Authors#