Reproducible benchmarking for multimodal single-cell integration, without making bioinformaticians become infrastructure engineers.
Multiverse is an MLOps platform for academic biological integration studies. You bring the scientific objects you already use in notebooks: AnnData, MuData, batch annotations, cell-type labels, and model questions. multiverse handles the repetitive infrastructure around registration, model execution, parameter tracking, artifacts, Optuna sweeps, and MLflow comparison.
The goal is simple: make it easier to run a defensible benchmark and easier to explain exactly what you did in a paper.
Multiverse is designed for researchers who are comfortable with Scanpy, Seurat, MuData, and Jupyter, but do not want every benchmark to become a Docker and orchestration project.
You should be able to answer scientific questions such as:
- Which integration model best preserves my annotated cell populations?
- Does batch correction remove donor or chemistry effects without erasing biology?
- Are conclusions stable across random seeds and hyperparameters?
- Can I attach enough provenance for a reviewer to reproduce my benchmark?
| You focus on | multiverse handles |
|---|---|
| Biological question and dataset curation | Dataset registration and compatibility checks |
| Batch and cell-type metadata | Metric eligibility and clear warnings |
| Model choice and hyperparameters | Safe, parallel execution without hand-running containers |
| Comparing embeddings and metrics | Results tables, artifacts, MLflow tracking, and Optuna sweeps |
| Writing a reproducible Methods section | run_manifest.yaml, job_spec.json, metrics, logs, and provenance artifacts |
- Prepare your
AnnDataorMuDataobject in Jupyter. - Save it under
store/datasets/<dataset-slug>/data/. - Open the Streamlit GUI.
- Register the dataset in the Registry tab.
- Choose dataset x model pairs in Job Builder.
- Set parameters in the Parameters tab.
- Launch the run in Run.
- Review metrics, embeddings, logs, and artifacts in Results and MLflow.
- Bring the selected
embeddings.h5back into Jupyter for figures and downstream analysis.
Prerequisites:
- Python 3.12+
uv- Docker with Compose v2
Set up the platform from a source checkout:
make bootstrap # install dev deps, initialize the registry, register built-in models
make services-up # optional: MLflow on :25000 and Optuna Dashboard on :28080
make setup # optional: install GUI and ML model wrapper extras
make build-evaluate # optional: prebuild the evaluation image used by Evaluate
make gui # launch Streamlit on :28501Then open the GUI at http://localhost:28501 (or the port in .env). For a headless run, generate or edit run_manifest.yaml and use:
uv run multiverse run --manifest run_manifest.yaml --output store/artifacts/run_outputOn HPC clusters with Slurm and Apptainer, use the Slurm path instead of Docker:
uv run multiverse slurm-submit \
--model-slug pca \
--image-sif /scratch/images/multiverse-pca-1.0.0.sif \
--image-digest sha256:<oci-digest> \
--params-json '{"n_components": 20}' \
--output store/artifacts/run_outputSee Runner & Orchestration for the full Slurm workflow including dual-digest artifact manifests.
In the GUI:
- Open the Registry tab.
- Expand Register New Dataset.
- Either provide an existing
dataset.yaml, or switch on Build manifest from fields. - Click Register Dataset.
- Open Job Builder and select compatible dataset x model pairs.
- Open Parameters and set model hyperparameters.
- Open Run and click Launch Run.
- In Run, use Evaluate Experiment after jobs reach
ARTIFACT_SUCCESS. - Open Results to inspect completed runs and artifact paths.
For a full guided tutorial, see Getting Started.
Most users should keep doing their exploratory work in notebooks. multiverse is meant to sit between notebook curation and notebook interpretation.
from pathlib import Path
import mudata as md
# Assume you already created these in Scanpy:
# adata_rna : AnnData with RNA counts/features
# adata_atac : AnnData with ATAC counts/features
dataset_dir = Path("store/datasets/pbmc_rna_atac")
data_dir = dataset_dir / "data"
data_dir.mkdir(parents=True, exist_ok=True)
mdata = md.MuData({
"rna": adata_rna,
"atac": adata_atac,
})
# Shared sample/cell metadata used by evaluation.
mdata.obs["batch"] = adata_rna.obs["batch"].astype(str)
mdata.obs["cell_type"] = adata_rna.obs["cell_type"].astype(str)
mdata.write_h5mu(data_dir / "processed.h5mu")Create the dataset manifest next to the data:
import yaml
manifest = {
"name": "PBMC RNA+ATAC",
"omics": ["rna", "atac"],
"raw_files": {
"rna": "data/processed.h5mu",
"atac": "data/processed.h5mu",
},
"metadata_keys": {
"batch": "batch",
"cell_type": "cell_type",
},
}
with open(dataset_dir / "dataset.yaml", "w") as f:
yaml.safe_dump(manifest, f, sort_keys=False)Then register store/datasets/pbmc_rna_atac/dataset.yaml in the GUI Registry tab.
After a successful run, copy the artifact path from the Results tab. The embedding file contains one HDF5 dataset named latent.
from pathlib import Path
import h5py
import mudata as md
import scanpy as sc
artifact_dir = Path("store/artifacts/run_output/store/artifacts/<artifact-id>")
# In practice, copy the exact artifact directory from the Results tab.
with h5py.File(artifact_dir / "embeddings.h5", "r") as f:
latent = f["latent"][:]
mdata = md.read_h5mu("store/datasets/pbmc_rna_atac/data/processed.h5mu")
adata = mdata["rna"].copy()
adata.obsm["X_multiverse_pca"] = latent
sc.pp.neighbors(adata, use_rep="X_multiverse_pca")
sc.tl.umap(adata)
sc.pl.umap(adata, color=["batch", "cell_type"])See Data Preparation for more detailed examples for AnnData, MuData, RNA+ATAC, and RNA+ADT studies.
multiverse does not decide what counts as appropriate biological preprocessing for your study. It makes your choices explicit and reproducible.
Recommended expectations:
- Use matrices in the state expected by the selected model. Count-based probabilistic models generally expect count-like input; PCA-style baselines are often run on normalized/log-transformed features.
- Keep raw counts available when possible, especially for RNA, ATAC, and ADT models that assume count data.
- Preserve feature annotations such as
feature_typeswhen models need to distinguish genes, peaks, and proteins. - Provide a
batch-like column for technical or donor effects you want assessed. - Provide a
cell_type-like column when you want supervised bio-conservation metrics such as ARI, NMI, label silhouette, or cLISI. - Store metadata columns as categorical or string-like values, not mixed Python objects.
- Record HVG selection and filtering choices in your notebook or supplementary methods.
The platform checks whether metadata columns exist and whether batch metrics are meaningful. It does not silently invent biological labels.
Every run is designed to leave a Methods trail:
run_manifest.yamlrecords the selected datasets, models, parameters, metrics, experiment name, and seed.job_spec.jsonrecords the exact runtime instructions passed to each model container.metrics.jsonrecords model-level metrics and histories when available.embeddings.h5records the latent representation used for downstream evaluation..multiverse/launches/<launch_id>/cohort.jsonrecords the full launch cohort and readiness inputs..multiverse/launches/<launch_id>/evaluations/<member_id>.jsonrecords one structured evaluation outcome per cohort member..multiverse/launches/<launch_id>/evaluation_report.jsonrecords the launch-level comparison table derived from per-member results.- logs and provenance artifacts document what ran and where outputs were written.
For a paper, include run_manifest.yaml and provenance.json when it is present in the run directory as Supplementary Material. Together with the archived input data, these files are the reproducibility contract for the benchmark: they record what was run, with which parameters, seed, metrics, and artifacts. In your Methods section, describe the dataset state, metadata keys, selected models, random seed, and metric families.
The docs follow Diátaxis:
| Type | Document | Purpose |
|---|---|---|
| Tutorial | Getting Started | First complete run from notebook object to results. |
| How-To | Data Preparation | Practical recipes for making data acceptable to multiverse. |
| How-To | Runner & Orchestration | Docker and Slurm execution paths, --accept-degraded, asset registry migration. |
| Reference | Models Glossary | Built-in model assumptions and hyperparameters. |
| Reference | Evaluation Metrics | Bio-conservation and batch-correction metric definitions. |
| Explanation | Architecture | How the platform works internally, including the dual-SQLite split and sole-writer invariant. |
| Explanation | Developer Guide | Codebase conventions, test suite, and core boundaries. |
- RNA+ATAC Integration for PBMC Multiome: prepare paired RNA and ATAC modalities, compare MultiVI, MOFA, Mowgli, Cobolt, and PCA, then inspect whether immune cell labels remain separated.
- CITE-seq RNA+ADT Protein Integration: prepare RNA and antibody-derived tags for TotalVI, evaluate protein-aware latent structure, and bring embeddings back into Scanpy.
- Cross-Donor Batch Correction in an Atlas Subset: register donor metadata as the batch key, compare models by bio-conservation and batch-correction metrics, and produce a reproducible Methods bundle.
Until a formal paper or DOI is available, cite the repository or archived release used for the benchmark. Include the multiverse version or commit hash, run_manifest.yaml, and run provenance artifacts with Supplementary Material.
Suggested wording:
Integration benchmarks were executed with multiverse (version/commit: <commit>). The full
benchmark recipe, including datasets, models, hyperparameters, random seed, and requested
metrics, is provided as Supplementary File X (`run_manifest.yaml`). Per-run provenance and
model artifacts are archived with the analysis.
Distributed under the MIT License. See LICENSE for details.
