Official code for Multi-Window Temporal Analysis for Enhanced Arrhythmia Classification: Leveraging Long-Range Dependencies in Electrocardiogram Signals (Physiological Measurement, 2026).
S4ECG applies hierarchical Structured State Space (S4) models to beat-level arrhythmia detection from long, single-lead ECG recordings. By jointly analyzing multiple consecutive 30-second ECG windows (up to 15 minutes), the architecture captures long-range temporal dependencies that isolated-window approaches miss. This multi-window strategy improves macro-averaged AUROC by 1.0–11.6 percentage points and reduces false positive rates for atrial fibrillation detection by 3–10×.
Raw ECG (128 Hz, 1ch)
└── EpochEncoder (CNN + S4) → epoch-level features
└── S4 Predictor → sequence-level features
└── MLP Head → per-epoch arrhythmia labels (N / AFIB / AFL)
conda env create -f environment.yml
conda activate s4ecgIf the S4 layer requires custom CUDA kernels for the Cauchy operation:
cd extensions/cauchy && python setup.py installThe model expects data in the memmap format produced by preprocessing/:
| Dataset | Use | Records | Source |
|---|---|---|---|
| LTAFDB | In-distribution train/val/test | 84 | PhysioNet |
| Icentia11k | In-distribution train/val/test | 11,000 | PhysioNet |
| MIT-BIH | Out-of-distribution test | 48 | PhysioNet |
| AFDB | Out-of-distribution test | 25 | PhysioNet |
Each dataset folder should contain:
memmap.npy # signal data (samples × channels)
memmap_meta.npz # memmap index
memmap_label.npy # annotation labels
memmap_label_meta.npz
df_memmap.pkl # DataFrame with fold assignments
lbl_itos.npy # label names ['(N', '(AFIB', '(AFL', ...]
mean.npy / std.npy # signal statistics
python main.py --config-name=config_id_ltafdb trainer.gpus=1python main.py --config-name=config_id_icentia trainer.gpus=1python main.py --config-name=config_ood_mitdb trainer.gpus=1 \
trainer.eval_only=path/to/best_model.ckptpython main.py --config-name=config_id_ltafdb \
base.batch_size=32 \
trainer.epochs=100 \
trainer.gpus=1All experiment configs live under conf/. The main configs are:
| Config | Dataset | Scenario |
|---|---|---|
config_id_ltafdb |
LTAFDB | In-distribution |
config_id_icentia |
Icentia11k | In-distribution |
config_ood_mitdb |
MIT-BIH | OOD evaluation |
Key parameters:
| Parameter | Value | Description |
|---|---|---|
base.fs |
128 | Sampling frequency (Hz) |
base.input_size |
115200 | Input window = 30s × 30 windows × 128 Hz (15 min) |
base.input_channels |
1 | Single-lead ECG |
label_aggregation_epoch_length |
3840 | 30s epoch = 30 × 128 |
stride_valtest |
3840 | Evaluation stride (one 30s epoch) |
loss |
bce | Binary cross-entropy (multi-label) |
metric |
auroc | Area under ROC curve |
s4ecg/
├── main.py # Hydra entry point
├── clinical_ts/ # Core library
│ ├── config.py # Hydra ConfigStore registration
│ ├── template_model.py # BaseModel / SSLModel (Lightning)
│ ├── template_modules.py # Config dataclasses, encoder wiring
│ ├── task/ecg.py # ECG-specific task class
│ ├── loss/supervised.py # CE, BCE, focal losses
│ ├── metric/base.py # AUROC, F1, accuracy, etc.
│ ├── data/ # Dataset loading & transforms
│ ├── ts/ # Encoder, predictor, head modules
│ └── utils/ # Callbacks, schedulers, evaluation
├── conf/ # Hydra configs
│ ├── config_id_ltafdb.yaml
│ ├── config_id_icentia.yaml
│ ├── config_ood_mitdb.yaml
│ ├── data/ # Per-dataset configs
│ ├── trainer/ # Training configs
│ └── ts/ # Model component configs
├── extensions/cauchy/ # Custom CUDA kernel for S4
├── preprocessing/ # Data preprocessing scripts
├── datasplit/ # Cross-validation fold assignments
└── environment.yml # Conda environment
@article{wang2026s4ecg,
title={Multi-Window Temporal Analysis for Enhanced Arrhythmia Classification: Leveraging Long-Range Dependencies in Electrocardiogram Signals},
author={Wang, Tiezhi and Haverkamp, Wilhelm and Strodthoff, Nils},
journal={Physiological Measurement},
volume={47},
number={1},
year={2026},
publisher={IOP Publishing},
doi={10.1088/1361-6579/ae3937},
pmid={41539004}
}MIT License — see LICENSE.