Skip to main content

ECGEN

ECG Generation Framework — PyTorch Lightning implementations of generative models for 12-lead electrocardiogram (ECG) signal synthesis and latent representation learning.

Overview

ECGEN trains and samples generative models of 12-lead ECGs through one library and one command (ecgen train <config.yaml>), on MIMIC-IV-ECG, PTB-XL, or your own data. Training methods, networks, and conditions are independent, swappable parts (see Concepts):

Family (model.name) Method
vae VAE / conditional VAE (spatial or global latent, learned prior)
pulse2pulse Pulse2Pulse WaveGAN (WGAN-GP); conditional and paired lead-reconstruction modes
vqvae, vqvae_prior VQ-VAE + PixelCNN prior (conditional, classifier-free guidance)
diffusion DDPM (ε / v / x₀, cosine / linear, min-SNR) with DDIM / ancestral sampling
flow_matching Rectified flow / conditional flow matching with Euler / Heun sampling, optional OT coupling
latent_diffusion, latent_flow_matching The same in the latent space of a frozen VAE or VQ-VAE

Backbones for diffusion / flow: unet1d, dit1d (transformer, AdaLN-Zero), ssm (SSSD-style S4D), mamba. Conditions: class labels and multi-label diagnostic statements, tabular / demographic / measurement fields (missing-aware), report text (hashed or a frozen Hugging Face encoder), and partial ECGs (any lead subset, time spans, forecasting) — with condition dropout, classifier-free guidance, and training-free inpainting (replace / RePaint).

ecgen train configs/experiments/diffusion_mimic_conditional.yaml
ecgen sample runs/diffusion_mimic_conditional/seed_42/checkpoints/last.ckpt -n 16 \
    --cond measurements=800,40,128,170,258,520,60,30,40 --guidance 2 --format csv -o samples/
ecgen evaluate samples/samples.npy configs/experiments/diffusion_mimic_conditional.yaml

The original Pulse2Pulse generator API (ecgen.generate) and the plotting helpers below are unchanged.


Installation

# From source
git clone https://github.com/vlbthambawita/ECGEN.git
cd ECGEN
pip install -e .

# With HuggingFace Hub support (for checkpoint uploads)
pip install 'ecgen[hf]'

Requirements: Python ≥ 3.8, PyTorch, PyTorch Lightning


Models

VAE — Variational Autoencoder

1D convolutional VAE with residual blocks for learning compact ECG representations. The encoder maps a 12-lead signal to a Gaussian latent space; the decoder reconstructs the signal from sampled latents.

Architecture: ResidualBlock1D → Encoder1D → latent (μ, σ) → Decoder1D

Loss: reconstruction_loss + kl_weight × KL_divergence

from ecgen.models import VAELightning, VAEConfig

config = VAEConfig(
    in_channels=12,
    base_channels=64,
    latent_channels=8,
    channel_multipliers=(1, 2, 4, 4),
    num_res_blocks=2,
    lr=1e-4,
    kl_weight=1e-4,
)
model = VAELightning(config)

# Generate new ECG signals
samples = model.sample(n_samples=16, seq_length=5000)  # (16, 12, 5000)

Pulse2Pulse — WaveGAN

Wasserstein GAN with gradient penalty for conditional ECG generation. Uses the first 8 leads of MIMIC-IV-ECG signals.

from ecgen.models import Pulse2PulseGAN, Pulse2PulseConfig

config = Pulse2PulseConfig(
    model_size=50,
    num_channels=8,
    seq_length=5000,
    lr=1e-4,
    lmbda=10.0,   # gradient penalty weight
    n_critic=5,   # discriminator steps per generator step
)
model = Pulse2PulseGAN(config)

Generating ECG Signals

Generate synthetic 8-lead ECG signals from any trained Pulse2Pulse checkpoint — local or directly from HuggingFace Hub:

from ecgen import generate

# From HuggingFace Hub — downloaded once, cached forever
paths = generate(
    model_path="hf://vlbthambawita/ECGEN/pulse2pulse/ptbxl/pulse2pulse_exp_ptbxl_full_epoch:900.pt",
    n_samples=10,
    output_dir="outputs/generated",
)
# → outputs/generated/sample_01.csv … sample_10.csv

# With interactive D3 HTML plots alongside each CSV (default)
generate(..., ecgplot=True)
# → sample_01.csv + sample_01.html …

# Static SVG plots
generate(..., ecgplot=True, plot_format="svg")

# PDF plots (requires: pip install 'ecgen[plot]')
generate(..., ecgplot=True, plot_format="pdf")
Parameter Default Description
model_path — Local .pt path or hf://owner/repo/path
n_samples — Number of ECGs to generate
output_dir — Output directory (created if absent)
format "csv" Signal output format
header True Lead-name header row in CSV (I,II,V1…V6)
ecgplot False Also save an ECG plot per sample
plot_format "html" "html" · "svg" · "pdf"
model_size 50 Generator model_size (must match training)
batch_size 32 Samples per GPU forward pass
device auto "cuda", "cpu", or None
denorm 1.0 Multiply raw output by this factor. Use 6000.0 for models trained on ECGDataSimple (÷6000 normalisation). Default 1.0 is correct for the PTB-XL checkpoint (physical mV, no normalisation).

ECG Plots

plot_ecg renders any ECG array as a real ECG graph-sheet — pink/red grid paper, labelled leads, 25 mm/s paper speed, 10 mm/mV scale:

from ecgen import plot_ecg
import numpy as np

ecg = np.load("my_ecg.npy")  # shape (8, 5000) or (5000, 8)

# Interactive HTML — pan/zoom with mouse/touch, save-SVG and Print buttons
plot_ecg(ecg, output_path="ecg.html", title="My ECG")

# Static SVG — embed in documents, no JS required
plot_ecg(ecg, output_path="ecg.svg", format="svg")

# PDF (requires: pip install 'ecgen[plot]')
plot_ecg(ecg, output_path="ecg.pdf", format="pdf")

# Return content string instead of writing a file
html_str = plot_ecg(ecg)
svg_str  = plot_ecg(ecg, format="svg")

The interactive HTML uses D3 v7 and is fully self-contained (single file). Features:

  • Scroll / pinch to zoom (x-axis); drag to pan
  • ECG grid scales with zoom so the mm² boxes always represent the same time/amplitude
  • ⤓ SVG button exports the current view as a clean SVG file
  • 🖨 Print / PDF button opens the browser print dialog (set destination to "Save as PDF")
Parameter Default Description
ecg — (n_leads, n_samples) or (n_samples, n_leads)
sample_rate 500 Hz
lead_names auto Defaults to ["I","II","V1"…"V6"]
title "ECG" Shown in toolbar and file header
output_path None Write to file; None returns the content string
format "html" "html" · "svg" · "pdf"
paper_speed 25.0 mm/s
amplitude_scale 10.0 mm/mV

Training

Every model family trains through one runner, driven by an experiment YAML in configs/experiments/. key=value overrides are applied after the YAML.

ecgen train configs/experiments/vae_mimic.yaml          # VAE
ecgen train configs/experiments/pulse2pulse_mimic.yaml  # Pulse2Pulse GAN
ecgen train configs/experiments/vqvae_mimic.yaml        # VQ-VAE stage 1
ecgen train configs/experiments/prior_mimic.yaml \
  model.params.vqvae_checkpoint=runs/vqvae_mimic/seed_42/checkpoints/last.ckpt  # stage 2

# Quick test with a small subset
ecgen train configs/experiments/vae_mimic.yaml \
  data.dataset.params.max_samples=1000 trainer.max_epochs=10 data.batch_size=16

# Smoke run without MIMIC
ecgen train configs/experiments/vae_mimic.yaml \
  data.dataset.name=synthetic "data.dataset.params={num_samples: 64}" trainer.accelerator=cpu

# Resume, inspect the resolved config, list registered models/datasets/callbacks
ecgen train configs/experiments/vae_mimic.yaml --resume runs/vae_mimic/seed_42/checkpoints/last.ckpt
ecgen train configs/experiments/vae_mimic.yaml --print-config
ecgen list

python -m ecgen train ... works without the console script. The shell wrappers in scripts/shell/ call the same runner.

Run outputs are saved to runs/{experiment_name}/seed_{seed}/ containing:

  • checkpoints/ — best and last model weights
  • samples/ — generated ECG batches
  • tb/ — TensorBoard logs

Config format

All experiments use a YAML config with target + params for dynamic instantiation:

experiment:
  name: vae_mimic
  seed: 42

model:
  target: ecgen.models.vae.VAELightning
  params:
    config:
      in_channels: 12
      latent_channels: 8
      kl_weight: 0.0001

data:
  target: ecgen.data.mimic_dataset.MIMICIVECGDataset
  params:
    mimic_path: /path/to/mimic-iv-ecg
    batch_size: 32
    max_samples: null   # null = full dataset

trainer:
  max_epochs: 100
  accelerator: gpu
  devices: [0]

Uploading Checkpoints to HuggingFace

pip install 'ecgen[hf]'
huggingface-cli login

From Python:

from ecgen.f import upload_hf, upload_hf_single

# Upload using a YAML config (supports subcategories like vae/ptbxl/)
upload_hf("configs/upload_checkpoints.yaml")

# Upload a single checkpoint
upload_hf_single(
    local_path="runs/vae/ptbxl/best.ckpt",
    repo_path="vae/ptbxl/best.ckpt",
    repo_id="your_username/ECGEN",
)

From CLI:

python scripts/upload_checkpoints_to_hf.py --dry-run   # preview
python scripts/upload_checkpoints_to_hf.py             # upload

Edit configs/upload_checkpoints.yaml to define which checkpoints map to which paths in the HF repo.


Package Structure

src/ecgen/
├── api.py          — ecgen.sample / ecgen.load_model
├── cli.py          — ecgen train | sample | evaluate | list
├── registry.py     — FAMILIES, DATASETS, CONDITION_ENCODERS, MASKS, BACKBONES, PROCESSES, CALLBACKS
├── types.py        — ECGBatch, ConditionOutput
├── data/           — datasets (MIMIC-IV-ECG, PTB-XL, synthetic), masks, normalizers, ECGDataModule
├── conditioning/   — field encoders (labels, numeric, text, masked signal), CompositeConditioner
├── families/       — vae, gan (Pulse2Pulse), vqvae (+ prior), continuous (diffusion, flow, latent)
├── processes/      — ddpm, rectified_flow
├── nn/             — blocks, injection layers (FiLM, AdaGN, AdaLN-Zero, cross-attention), backbones
├── training/       — runner, callbacks, EMA
├── evaluation/     — measurements, distribution / adherence / reconstruction metrics, classifier
├── generate.py     — legacy Pulse2Pulse generator; plot.py — ECG plots; f/ — HF upload
└── models/         — deprecated shims to families/

Research framework

Besides the published ecgen package, the repository root holds the research code used for new experiments. These packages are not part of the PyPI wheel; run scripts and tests from the repo root:

models/         VAE, VQ-VAE + PixelCNN prior, Pulse2Pulse GAN (diffusion/SSM placeholders)
data/           Dataset implementations (MIMIC-IV-ECG)
training/       Losses, callbacks, trainers
evaluation/     Evaluation metrics and benchmarking
visualization/  ECG, latent-space and training-curve plots
utils/          Config helpers
configs/        Experiment YAMLs (configs/experiments/*.yaml)
scripts/        train/, generate/, evaluate/, shell/ wrappers
single_file/    Self-contained VQ-VAE (both stages) in one script
pip install -e ".[dev]"
pytest tests/

# VQ-VAE stage 1, then the PixelCNN prior (stage 2)
./scripts/shell/run_train_vqvae_mimic.sh
./scripts/shell/run_train_vqvae_prior_mimic.sh runs/vqvae_mimic/seed_42/checkpoints/last.ckpt

Full documentation: https://vajira.info/ECGEN/ (source in docs/, built with MkDocs Material).


Releasing to PyPI

The package is published as ecgen automatically on tagged commits:

# The version comes from the tag (setuptools-scm); no file to bump
git tag v0.2.0
git push origin v0.2.0

GitHub Actions builds and publishes to PyPI via OIDC trusted publishing (no API token required).


License

See LICENSE.

Metadata

Release files for ecgen 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ecgen 1.1.0
File Size Uploaded Provenance Actions
ecgen-1.1.0.tar.gz 411.1 kB With provenance Details for ecgen-1.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ecgen 1.1.0
File Interpreter ABI Platform Actions
ecgen-1.1.0-py3-none-any.whl Python 3 none any Details for ecgen-1.1.0-py3-none-any.whl

Total release size: 562.8 kB

Release files / ecgen-1.1.0.tar.gz

Download URL ecgen-1.1.0.tar.gz
Size 411.1 kB
Tags Source
SHA-256 checksum
How to use checksums
36cf443fbe7a680410c6473f1d33a1137a19ae46a8d8baac01c2d6e6e5537937
BLAKE2b-256 checksum
How to use checksums
9f423e31e0cb8a21e37ab8f66bb31b7421f0f1b8f6a8ed598010a39deab4ac3b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / ecgen-1.1.0-py3-none-any.whl

Download URL ecgen-1.1.0-py3-none-any.whl
Size 151.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6e6e12b6a2a22f099f81019228b1b1bc2931ad1b87f07688145ff08c0261c660
BLAKE2b-256 checksum
How to use checksums
fb7eb2ff251789eb5d66f74f7085e4f1f9144540b15d5753e86e719c661783d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Security for ecgen 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ecgen-1.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page