Skip to main content

pyvae

Informed Variational Autoencoder (iVAE) for gene expression, in PyTorch.

pyvae implements a VAE whose encoder is biologically constrained: each gene can only influence the pathways that contain it, according to the Reactome database. The result is a latent space whose dimensions correspond to interpretable biological pathways rather than opaque features.

CI License: MIT conda-forge PyPI Version


Why an informed VAE?

A standard Variational Autoencoder learns a compressed latent representation $z$ of the input $x$ by maximising the Evidence Lower Bound (ELBO):

$$ \text{ELBO} = \mathbb{E}_{q(z|x)}[\log p(x|z)] - \text{KL}(q(z|x),|,p(z)) $$

Gene expression is high-dimensional (~20 000 genes) but structured: genes act together in pathways. pyvae restricts the encoder's first layer so only the gene → pathway connections that exist in Reactome are allowed. A binary mask enforces this on the weight matrix: $W_{\text{eff}} = W \odot M$, where $M_{ji}=1$ iff gene $i$ belongs to pathway $j$. This reduces parameters, injects prior biological knowledge, and makes each latent node interpretable as a pathway.

Features

Models

InformedLinear is a masked linear layer that enforces arbitrary connectivity priors. InformedVAE builds a full encoder/decoder around it, using the reparameterisation trick and a combined reconstruction, KL, and L2 loss. You configure it through constructor kwargs. likelihood picks between "gaussian" (MSE on log1p-normalized expression, the default) and "nb" (a negative-binomial likelihood on raw counts, implemented scVI-style in pure PyTorch with no scvi-tools dependency). n_cov sets the width of an auxiliary one-hot covariate (cell type, condition, or both), which the model concatenates into the encoder and decoder to support conditional inference.

Training

train_ivae is the legacy training loop: gradient clipping plus early stopping. train_ivae_modern trains the count model instead, adding KL warmup, AdamW with decoupled weight decay, cosine LR annealing, and best-weight restore.

Interpretation

InformedVAE.predict_counterfactual (NB models only) encodes a cell under one covariate and decodes it under another, returning predicted counts. It uses the posterior mean rather than a stochastic sample, so the prediction stays deterministic, which is what you want for a question like "what would this control cell look like if stimulated?" bayes_factor_da computes a signed Bayes factor per pathway between two groups of cells, based on Monte-Carlo pairing of raw pathway activations. integrated_gradients attributes a single pathway's activation back to each input gene (Sundararajan et al., 2017), implemented in pure PyTorch.

Data helpers

Reactome helpers (build_model_config, sync_gexp_adj) and a Kang PBMC dataset loader (load_kang) cover end-to-end pipelines. swap_condition flips the one-hot condition columns in a covariate DataFrame, which is how you build the cov_to argument for predict_counterfactual. set_all_seeds seeds Python's random, NumPy, and PyTorch (CPU and CUDA) in a single call, for reproducible runs.

Installation

pip install pyvae                      # from PyPI
conda install -c conda-forge pyvae     # from conda-forge
pixi add pyvae                         # into a pixi project

pyvae is a pure-Python, noarch package and runs on Linux, macOS, and Windows.

GPU acceleration

pyvae only requires torch>=2.3,<3 and never pins a platform. Which hardware you can accelerate depends entirely on which PyTorch build your install command pulls. Use this table to pick the right path:

Your machine pip install pyvae conda install -c conda-forge pyvae Accelerated out of the box?
NVIDIA CUDA (Linux/Windows) default torch wheel is the CUDA build conda-forge ships CUDA pytorch variants ✅ yes, on both
Apple Silicon (macOS arm64) standard wheel includes MPS (Metal) conda-forge osx-arm64 build includes MPS ✅ yes (use device="mps")
AMD ROCm (Linux) gets the CUDA wheel, which won't drive an AMD GPU conda-forge has no ROCm builds → CPU only ❌ no (see ROCm steps below)
Intel macOS / generic CPU CPU wheel CPU build n/a (CPU only)

CUDA and Apple-Silicon users need no extra steps. Verify at runtime with python -c "import torch; print(torch.cuda.is_available())" (CUDA) or torch.backends.mps.is_available() (macOS).

AMD ROCm users must install torch from PyTorch's ROCm index. ROCm wheels exist neither on PyPI nor on conda-forge, so conda install is a dead end for AMD GPUs. Install ROCm torch first, then pyvae; pip then sees the constraint already satisfied and leaves torch alone:

pip install torch torchvision torchaudio \
  --index-url https://download.pytorch.org/whl/rocm7.2   # match your ROCm runtime
pip install pyvae

To pin an explicit CUDA stack via conda instead of relying on auto-detection:

conda create -n pyvae-cuda12 -c conda-forge -c pytorch -c nvidia \
  pyvae pytorch pytorch-cuda=12.*

Match the CUDA/ROCm version to your driver/runtime; the official PyTorch selector gives exact wheels.

Quickstart

import pandas as pd
import torch
import pyvae

# 1. Load and preprocess data (Kang PBMC)
adata = pyvae.load_kang(data_folder="./data", n_genes=3000)

# 2. Train/validation split
train_mask = adata.obs["split"] == "train"
x_train = pd.DataFrame(adata[train_mask].X.toarray(), columns=adata.var_names)
x_val   = pd.DataFrame(adata[~train_mask].X.toarray(), columns=adata.var_names)

# 3. Build the biological (Reactome) configuration and adjacency
config = pyvae.build_model_config(
    genes=x_train.columns,
    model_kind="ivae_reactome_hierarchical_d2",
    resources_dir="./resources",
)
adj = torch.tensor(config.model_layer[0].values, dtype=torch.float32)

# 4. Build and train the model
model = pyvae.InformedVAE(adj=adj, latent_dim=64, l2_lambda=1e-5)
model, history = pyvae.train_ivae(
    model, x_train, x_val,
    epochs=500, batch_size=32, patience=50, lr=1e-4, device="cpu",
)

# 5. Encode new cells — `h` is the interpretable pathway activation
z_mean, z_log_var, h = model.encode(torch.tensor(x_val.values, dtype=torch.float32))

Development

This repo is managed with pixi. The manifest defines several environments (default, ci, cuda12, rocm72, publish):

pixi install              # create the default environment
pixi run pytest tests/ -v # run the test suite
pixi run fixcode          # ruff lint + import sort
pixi run formatcode       # ruff format

Without pixi, a standard editable install works too:

pip install -e ".[dev]" && pytest tests/ -v

Releasing

This repo publishes to both PyPI and conda-forge. Before releasing, bump the version in pyproject.toml (both [project].version and [tool.pixi.package].version).

PyPI (Trusted Publishing in CI on a v* tag, or locally via the isolated publish env):

pixi run -e publish build-dist     # build sdist + wheel into dist/
pixi run -e publish check-dist     # twine metadata check
pixi run -e publish publish-pypi   # upload (set TWINE_REPOSITORY=testpypi for TestPyPI)

conda (built as a single noarch package by pixi):

pixi publish --target-dir ./conda-dist          # build + copy locally (no upload)
pixi publish --to https://prefix.dev/<channel>  # or push to a channel directly

The pyvae-feedstock handles conda-forge distribution instead of a direct upload, and the package is already live there. regro-cf-autotick-bot picks up new PyPI releases automatically and opens a version-bump PR on the feedstock, so no action is needed here. The original submission recipe and instructions for manual recipe changes live in conda-recipe/. On a tagged release, .github/workflows/release.yml builds and verifies both artifacts and attaches the noarch conda package to the GitHub Release.

Project layout

pyvae/
├── __init__.py    public API
├── utils.py       seeding utilities
├── layers.py      InformedLinear (masked layer)
├── components.py  Encoder, DenseDecoder, CountDecoder, likelihoods, helpers
├── models.py      InformedVAE (composition of components)
├── train.py       training loops: train_ivae, train_ivae_modern
├── datasets.py    Kang dataset loader
├── bio.py         Reactome adjacency helpers, swap_condition
└── interpret.py   bayes_factor_da, integrated_gradients
tests/             contract + unit tests
conda-recipe/      conda-forge recipe + submission guide

Acknowledgements

pyvae is a PyTorch reimplementation that builds on earlier Keras informed-VAE work directed by Carlos Loucera, with contributions from Pelin Gundogdu (babelomics/ivae_scorer), Alberto Esteban-Medina (albertoem77/robustness_informed_TFM), and Sara Fernandez (saraafdezz/robustness_informed_TFG).

License

MIT © Amir Aynede, Carlos Loucera

Metadata

Release files for pyvae 0.2.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 pyvae 0.2.0
File Size Uploaded
pyvae-0.2.0.tar.gz 26.7 kB Details

Built distribution (wheel)

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

Total release size: 50.1 kB

Release files / pyvae-0.2.0.tar.gz

Download URL pyvae-0.2.0.tar.gz
Size 26.7 kB
Tags Source
SHA-256 checksum
How to use checksums
9455e8af538d71a55fb97df1b103f1673b858fe3762db98ca0d96d3864ff54bd
BLAKE2b-256 checksum
How to use checksums
7ac476feecb9a18b072a874e4d7281dd27be6ffee88593c487c4feed26f72868
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.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 Jul 23, 2026.

Transparency log

Release files / pyvae-0.2.0-py3-none-any.whl

Download URL pyvae-0.2.0-py3-none-any.whl
Size 23.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
29d8d304ac211ea133bbf0670d0483be667071f6b69fe5f1da87e65723eff1ad
BLAKE2b-256 checksum
How to use checksums
7f5e527094063433c6a0549acc482376541027e0681af44d6b703deedf447c57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.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 Jul 23, 2026.

Transparency log

Release history Release notifications | RSS feed

1.0.0

2 release files

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.1

2 release files

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