Skip to main content

mirpy

mirpy — ML embeddings for immune repertoires

PyPI Python CI docs License

Get started · Guides · Commands · API · Theory · Examples · Glossary

Turn T- and B-cell receptor repertoires into fixed-length numeric vectors you can cluster, visualise, and hand to a machine-learning model — at the scale of one receptor, one repertoire, or a whole cohort.

mirpy implements TCREMP: each receptor is embedded by its alignment distances to a fixed set of prototype sequences, so Euclidean distance in embedding space approximates pairwise alignment distance. That turns a set of variable-length strings into a coordinate system, which is what every downstream method actually needs.

PyPI package mirpy-lib, import name mir. Pure Python; the alignment, Pgen and sampling work is reused from seqtree and vdjtools.

Install

pip install mirpy-lib

Optional extras, each for one job:

pip install "mirpy-lib[ann]"     # pynndescent: approximate neighbours at whole-repertoire scale
pip install "mirpy-lib[ml]"      # torch: the neural codecs and the learned set encoder
pip install "mirpy-lib[bench]"   # the benchmark harness and theory experiments
pip install "mirpy-lib[examples]"  # marimo, matplotlib, umap-learn: to run the notebooks

Three scales

Choosing the right scale is most of the work:

Scale One unit is You get Module
Clonotype one receptor a point in prototype space mir.embedding
Repertoire one sample from one donor one fingerprint, Φ(S) mir.repertoire
Cohort many donors, several chains each one comparable matrix mir.cohort

Quick start

import polars as pl
from mir.embedding.tcremp import TCREmp

model = TCREmp.from_defaults("human", "TRB")     # per-chain preset: 2000 prototypes
df = pl.DataFrame({
    "v_call":      ["TRBV10-3*01",   "TRBV20-1*01"],
    "j_call":      ["TRBJ2-7*01",    "TRBJ1-2*01"],
    "junction_aa": ["CASSIRSSYEQYF", "CSARVSGYYGYTF"],
})
X = model.embed(df)          # (2, 6000) float32 — three distances per prototype

Denoise, then cluster:

from mir.embedding.pca import pca_denoise
from mir.bench.metrics import cluster
labels = cluster(pca_denoise(model.embed(vdjdb_df), n_components=65))

From the shell:

mir embed clonotypes  sample.tsv      -o clonotypes.parquet --pca 50
mir embed repertoires cohort/*.tsv.gz -o phi.tsv --mmd mmd.tsv
mir signature --corpus blood cohort/*.tsv.gz -o rsig.parquet

Getting started walks through all of this in about fifteen minutes, offline, with nothing to download.

Where to start

You want to Go to
Embed receptors and cluster antigen-specific ones Getting started
Pick a prototype count and a PCA dimension Which prototypes
Find enriched or antigen-driven neighbourhoods mir.density
Compare whole repertoires rather than clonotypes mir.repertoire
Get one fixed, named feature vector per sample Signatures
Name which part of a vector carries a signal Channels
Understand why any of this is valid Theory
Look up a command or a function Commands · API
Check what a term means Glossary

What is in the box

Module What it does
mir.embedding TCREmp and PairedTCREmp, PCA denoising, the bundled prototypes and per-chain presets
mir.distances Junction distance through seqtree.gapblock, plus precomputed arda germline distances
mir.density Graph-free TCRNET and ALICE neighbourhood enrichment in embedding space, with exact, kdtree and approximate backends
mir.repertoire One vector per sample: kernel mean, coverage-standardised Hill diversity, second moment; MMD distance, motif witness, sub-probability measures
mir.signature The portable signature — fixed, named, already-standardised columns that join to vdjtools' half on sample_id
mir.explain Which named channel carries a signal under your scorer, and which clonotypes drive it
mir.cohort The digital donor — multi-chain donor embeddings, batch residualisation, sample clustering
mir.track Exposure trajectory — a latent progression axis disentangled from a known covariate
mir.generate, mir.twin Sample new synthetic donor states, or perturb and resample one donor's state
mir.ml Neural codecs, learned set encoders, diffusion generator — experimental, [ml] extra
mir.bench VDJdb loader, clustering metrics, cohort scorers, reproduced theory experiments
mir.cli The mir console script — embed clonotypes, embed repertoires, signature, corpus

Prototypes are the coordinate system

Every embedding is distances to prototypes, so the prototype set is the coordinate system. mirpy ships 10,000 real receptors per chain — a fixed-seed uniform sample of unique, productive, germline-resolvable clonotypes from arda-annotated repertoires, for human TRA/TRB/TRG/TRD/IGH/IGK/IGL and mouse TRA/TRB. Nothing is downloaded at import or build time.

Real rather than model-generated, because synthetic junctions have degenerate lengths and embed measurably worse. To ask whether a result depends on which prototypes you drew, take a replicate: the geometry agrees at R = 0.993 between two independent draws at the default n=1000, and 0.997 at n=2000, so from about n≈500 up the draw is not what your result rests on.

Each replicate is a different coordinate system — distances within one are comparable, across two are not, and every container refuses a prototype-hash mismatch rather than mixing them silently. Details and the full presets table.

Repertoire fingerprints

One fixed vector Φ(S) per repertoire, robust down into the low-coverage bulk-RNA-seq regime. It sketches the repertoire as a weighted cloud of receptor points in three blocks: a random-feature kernel mean, a coverage-standardised Hill diversity profile, and a second-moment block carrying co-occurrence structure. Distance between two repertoires is the MMD.

from mir.repertoire import fit_repertoire_space, sample_embedding, mmd_matrix

space = fit_repertoire_space(model, pooled_clonotypes)   # ONE basis for the whole cohort
embs  = [sample_embedding(space, s) for s in samples]
D     = mmd_matrix(embs, unbiased=True)

Every sample in a cohort must go through one prototype set and one basis, or the distances are not comparable. Use unbiased=True whenever depths differ: the biased estimator's 1/n_eff self-term inflates low-diversity samples and manufactures signal with the wrong sign.

Signatures

Φ(S) is fitted on your cohort, which is what makes it powerful and also what makes it incomparable with anyone else's. When you need a vector a collaborator can reproduce independently, use a signature instead: fixed, named columns standardised against a published reference corpus.

mir       signature --corpus blood cohort/*.tsv.gz -o rsig.tsv   # the geometry half
vdjtools  signature --corpus blood cohort/*.tsv.gz -o vsig.tsv   # the statistics half

The two join on sample_id. Nine corpora are published, all at 256 components per locus; artifacts are fetched and digest-verified on first use. --corpus is required — a signature is comparable to another one only if both were rotated through the same corpus. Signatures · Channels

Performance

CPU-parallel by default; the GPU is used only by mir.ml. Embedding runs the C++ seqtree.gapblock scorer at about 530 million pairs per second on 16 cores and releases the GIL. Density defaults to an exact multicore kd-tree; the approximate backend is about 30× faster past 1e5 clones and is deliberately asymmetric — only the observed side is approximate, which biases enrichment down, while the background is always exact. Full table of parallelism settings.

Examples

Six runnable marimo notebooks — plain Python files, so they diff like source:

pip install "mirpy-lib[examples]"
marimo edit examples/signature_pipeline.py     # start here

signature_pipeline (a folder of AIRR files to a joinable table — read this one first) · quickstart (embed, denoise, cluster, UMAP) · signature (how the halves differ, and how many components survive a refit) · density (enrichment against a background model) · trajectory_and_twin (time courses and counterfactual donors) · theory (the results the library rests on). See the gallery.

Development

Repo-local .venv via uv:

bash setup.sh --dev-parents --tests    # editable-installs sibling seqtree / vdjtools checkouts
python -m pytest tests/ -q

See CLAUDE.md for the architecture and the reuse map.

Citing

Method: Kremlyakova et al., TCREMP: a bioinformatic pipeline for efficient embedding of T-cell receptor sequences, J Mol Biol 437 (2025) 169205.

The theory this implements is collected in the mathematical foundations; derivations and recorded benchmark numbers live in the companion analysis repository 2026-mirpy-analysis, with the LaTeX appendix in 2026-mirpy-ms.

License

GPL-3.0-or-later. The classical v1.x/v2 repertoire toolkit — parsing, overlap, diversity, TCRnet, GLIPH — is frozen on the legacy-v2 branch (mirpy-lib 2.x); its functionality lives on in vdjtools and vdjmatch.

Running signatures across samples

mir signature --corpus blood --jobs 8 samples/*.tsv -o signatures.parquet

--jobs selects worker threads: 1 runs without a pool, 0 uses allocated cores, and positive counts must fit that allocation. Workers share frozen resources and use serial inner kernels. They read one sample at a time; output follows input order. Embeddings use fixed 4,096-row blocks (24 MiB at 256 prototypes).

The Python cohort API accepts zero-argument readers, including closures and lambdas, to defer file loading. Callbacks must not mutate shared resources. Import mir before numerical libraries so their persistent pools start with one thread.

Density analysis also accepts an explicit kernel budget: neighbor_enrichment(observed, background, threads=8). Background counts are exact with both KD-tree and ANN observation queries, including points tied at the selected radius. Only ANN observation-neighbor recall is approximate.

CPU budgets

Import mir before numerical libraries in each fresh worker. Persistent numerical pools default to one thread; native embedding and density kernels receive explicit allocation-aware budgets. Use threads=1 for serial execution, threads=0 for the allocated CPUs, or a positive count within that allocation. Sample thread workers use one kernel thread each. Excessive or nested budgets raise instead of spawning unrestricted pools. See the usage guide for already-initialized Polars/Numba pools.

Metadata

Release files for mirpy-lib 4.5.1

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

Source distribution (sdist)

Source distribution for mirpy-lib 4.5.1
File Size Uploaded
mirpy_lib-4.5.1.tar.gz 5.7 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for mirpy-lib 4.5.1
File Interpreter ABI Platform
mirpy_lib-4.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 7.8 MB

Release files / mirpy_lib-4.5.1.tar.gz

Download URL mirpy_lib-4.5.1.tar.gz
Size 5.7 MB
Tags Source
SHA-256 checksum
How to use checksums
2e0d3f4b919acecc250d92e5c596d1cc820f675c64c1fdeee0cb731e608af98e
BLAKE2b-256 checksum
How to use checksums
6684f147449ffed5d9d7c95dade350b11c7c0257017c5ba9b70c3044473b5bb3
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 11, 2026.

Transparency log

Release files / mirpy_lib-4.5.1-py3-none-any.whl

Download URL mirpy_lib-4.5.1-py3-none-any.whl
Size 2.1 MB
Tags Python 3
SHA-256 checksum
How to use checksums
66230d4af694e70b04b12a89156dd6b18e5757f1f7694c477267a7d046bda9a0
BLAKE2b-256 checksum
How to use checksums
9b964beaf360654c5918765e236cdc69579861cd48634df664ec80f0fa15eb5f
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 11, 2026.

Transparency log

Release history Release notifications | RSS feed

4.7.0

2 release files

4.6.0

2 release files

This release

4.5.1 This release

2 release files

4.5.0

2 release files

4.4.1

2 release files

4.4.0

2 release files

4.3.0

2 release files

4.2.0

2 release files

4.1.0

2 release files

4.0.0

2 release files

3.20.2

2 release files

3.20.1

2 release files

3.20.0

2 release files

3.19.0

2 release files

3.18.0

2 release files

3.17.0

2 release files

3.16.0

2 release files

3.15.0

2 release files

3.14.0

2 release files

3.13.0

2 release files

3.12.0

2 release files

3.10.0

2 release files

3.9.1

2 release files

3.9.0

2 release files

3.8.1

2 release files

3.7.0

2 release files

3.6.0

2 release files

3.4.0

2 release files

1.1.1

10 release files

1.0.0

15 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