UniVI integrates single-cell modalities measured in the same cells (RNA, surface protein, chromatin accessibility, DNA methylation, and others) into one latent space. Each modality has its own encoder and decoder, so a trained model can align cells across assays, predict one modality from another, map new single-modality datasets onto a multimodal reference, and generate cells.
Documentation, tutorials, and API reference: univi.readthedocs.io
pip install univi # or: conda install -c conda-forge univi
pip install "univi[tutorials]" # adds Jupyter and scikit-misc for the tutorialsUniVI requires PyTorch. If import torch fails, or you need a specific CUDA build, install PyTorch first from pytorch.org.
Train on paired RNA + ATAC from 10x Multiome PBMCs (downloaded and cached on first use), then embed held-out cells and predict their gene expression from chromatin accessibility:
import torch
import univi.datasets as uds
from univi import ModalityConfig, TrainingConfig, UniVIConfig, UniVIMultiModalVAE, UniVITrainer
from univi.evaluation import cross_modal_predict, encode_adata, evaluate_alignment
from univi.preprocessing import ATACPreprocessor, RNAPreprocessor, split_by_label
from univi.workflows import make_loader, save_reference
device = "cuda" if torch.cuda.is_available() else "cpu"
# 1. Paired data: one AnnData per modality, same cells in the same order
data = uds.pbmc_multiome_10k()
rna, atac = data["rna"], data["atac"]
# 2. Split, then fit preprocessing on training cells only
split = split_by_label(rna.obs["cell_type"], train_fraction=0.8, val_fraction=0.1, seed=0)
rna_prep = RNAPreprocessor(n_hvg=2000, scale=True).fit(rna[split["train"]])
atac_prep = ATACPreprocessor(n_components=101, drop_first=True).fit(atac[split["train"]])
parts = {k: {"rna": rna_prep.transform(rna[i]), "atac": atac_prep.transform(atac[i])}
for k, i in split.items()}
train, val, test = parts["train"], parts["val"], parts["test"]
# 3. One encoder/decoder per modality, a shared 30-dimensional latent space
cfg = UniVIConfig(
latent_dim=30, beta=1.25, gamma=4.35, encoder_dropout=0.1, decoder_dropout=0.05,
kl_anneal_start=50, kl_anneal_end=85, align_anneal_start=75, align_anneal_end=110,
modalities=[
ModalityConfig("rna", train["rna"].n_vars, [512, 256, 128], [128, 256, 512], likelihood="gaussian"),
ModalityConfig("atac", train["atac"].n_vars, [128, 64], [64, 128], likelihood="gaussian"),
],
)
model = UniVIMultiModalVAE(cfg)
# 4. Train with early stopping on the validation cells
UniVITrainer(
model,
train_loader=make_loader(train, batch_size=256, shuffle=True, drop_last=True),
val_loader=make_loader(val, batch_size=1024),
train_cfg=TrainingConfig(n_epochs=400, lr=1e-3, weight_decay=1e-4, device=device,
early_stopping=True, patience=50, best_epoch_warmup=110),
).fit()
# 5. Embed each modality, measure alignment, predict RNA from ATAC
z_rna = encode_adata(model, test["rna"], modality="rna", latent="modality_mean", device=device)
z_atac = encode_adata(model, test["atac"], modality="atac", latent="modality_mean", device=device)
labels = test["rna"].obs["cell_type"].astype(str).to_numpy()
metrics = evaluate_alignment(Z1=z_rna, Z2=z_atac, labels_source=labels, labels_target=labels)
print("FOSCTTM:", metrics["foscttm_mean"], "label transfer:", metrics["label_transfer_acc"])
rna_from_atac = cross_modal_predict(model, test["atac"], src_mod="atac", tgt_mod="rna", device=device)
# 6. Save weights + configuration + fitted preprocessing as a reusable reference
save_reference("multiome_reference", model, preprocessors={"rna": rna_prep, "atac": atac_prep})The quickstart notebook runs the same steps with plots and explanations.
| Task | Tutorial |
|---|---|
| Integrate paired RNA + ATAC; check alignment; predict one modality from another | Quickstart |
| CITE-seq: fused embeddings, protein prediction, denoising, per-cell modality weights | CITE-seq |
| Map RNA-only or ATAC-only data onto a reference; transfer labels; flag unfamiliar cells | Query mapping |
| Add cell-type or genotype heads to a trained reference, with partial labels | Heads and refinement |
| Denoise, generate cells, run in-silico perturbations | Generation |
| Three or more modalities; count, methylation (beta-binomial) and other likelihoods | Custom modalities |
Every tutorial opens in Google Colab. The user guide covers data preparation, likelihood and hyperparameter choices, evaluation metrics, and saving models.
The notebooks that produced every figure of the article are in notebooks/GR_manuscript_reproducibility/, with parameter files in parameter_files/ and a driver script in scripts/revision_reproduce_all.sh. The article was prepared against release v0.4.7 (pip install univi==0.4.7, or envs/univi_v0.4.7_env.yml). The reproduction guide maps each figure to its notebook and dataset.
Processed datasets download with univi.datasets (uds.list_datasets() shows what is available); see the dataset catalog for all seven records. The annotated Multiome PBMC data are on Zenodo, and the paired scNMT-seq RNA/CpG/GpC data for Supplemental Fig. S4 are on Zenodo 10.5281/zenodo.22885463. Load the latter with uds.load("scnmt_gastrulation") (1,140 paired cells; success/coverage layers and train/validation/test assignments included).
Ashford AJ, Enright T, Somers J, Nikolova O, Demir E. Unifying multimodal single-cell data with a mixture-of-experts β-variational autoencoder framework. Genome Research 36(10): 2068–2090 (2026). doi:10.1101/gr.281431.125
@article{Ashford2026UniVI,
title = {Unifying multimodal single-cell data with a mixture-of-experts
{$\beta$}-variational autoencoder framework},
author = {Ashford, Andrew J. and Enright, Trevor and Somers, Julia
and Nikolova, Olga and Demir, Emek},
journal = {Genome Research},
year = {2026},
volume = {36},
number = {10},
pages = {2068--2090},
doi = {10.1101/gr.281431.125},
url = {https://doi.org/10.1101/gr.281431.125}
}univi/ the Python package
docs/ documentation source; tutorials are the notebooks in docs/tutorials/
notebooks/ archived analysis notebooks for the Genome Research article
parameter_files/, scripts/ JSON configurations and command-line entry points
envs/ conda environment files (including the v0.4.7 environment used for the article)
tests/ unit tests and tutorial execution tests
Questions and bug reports are welcome as GitHub issues; please include the UniVI version (python -c "import univi; print(univi.__version__)") and a minimal example. See CONTRIBUTING.md for the development setup.
UniVI is released under the MIT License.