Work with releases and repertoires#

Current annotation CLI#

vdjmatch annotate is the current cohort entry point. Its default --reference-population all-valid uses every valid source observation, retains exact evidence and combines linked paired references with unlinked marginal support. Choose --reference-population curated-benchmark explicitly for the source-curated benchmark population. See Annotate your first receptors and Examples gallery for raw inputs and reproducible offline commands.

Legacy Annotator API: choose a reference#

The following Annotator examples describe the retained candidate/E-value API, which is separate from the targeted stem4 CLI kernel and its final-call calibration.

from vdjmatch import Annotator

latest = Annotator.latest(asset="default", cache="reference-inputs")
pinned = Annotator.version("YOUR_RELEASE_TAG", cache="reference-inputs")
local = Annotator.from_path("reference.zip", asset="default")
extracted = Annotator.from_path("reference-directory", asset="primary")
table = Annotator.from_path("reference.tsv")

Local paths resolve without downloading a release. Supported inputs include legacy tables, release ZIPs and extracted directories, rich records/chains bundles, and AIRR Rearrangement/Reactivity bundles. Bundle manifests and schema metadata identify the appropriate assets and joins. Incomplete or incompatible inputs raise an error rather than silently choosing a different reference.

default selects the release’s default supported representation. primary, legacy and airr select bundle roles when provided; slim and full select the corresponding legacy projections. Request a representation that the chosen release supplies. A new bundle supported by the resolver is not necessarily available in the latest public release.

Reference rows with an invalid amino-acid junction are retained with reference_valid=False and excluded from the search index. The reference report states retained/excluded counts. Inspect that report when assessing reference coverage.

The legacy Annotator API reference curation excludes KLGGALQAK and the engineered phage-library study PMID 40498839 from search and background populations. Source rows remain available with reference_exclusion_reason; natural SLLMWITQV observations from other studies remain searchable. This legacy policy uses peptide/study identity, never a sequence-frequency or motif filter on natural receptors. It is not the current annotate CLI all-valid default.

An HF mirror is selected explicitly with source="hf"; it is distinct from the GitHub latest/pinned release source. Reuse an Annotator to keep one explicit reference index across calls.

Filter a reference before indexing#

db.load and Annotator.from_path accept epitope, mhc_a, mhc_b and reference_id as a string or list of strings. Values within a selector are alternatives; different selectors must all match. exclude_reference_ids removes specified studies while retaining rows without a study identifier.

restricted = Annotator.from_path(
    "reference.zip", epitope=["GILGFVFTL", "NLVPMVATV"],
    mhc_a="HLA-A*02:01", exclude_reference_ids=["study-to-exclude"],
)

evidence_type selects records with any attached evidence observation of a requested type, retaining every selected source chain and its complete evidence metadata. It accepts the same string/list convention. For a rich release, for example, evidence_type="structure_native" requires that type in the attached evidence. A requested evidence predicate raises when the reference does not supply an evidence_type field; it never silently ignores an unavailable predicate. These selectors retain source order and do not deduplicate reference observations.

Read AIRR and custom tables#

from vdjmatch.io import read_rearrangement

queries, report = read_rearrangement(
    "repertoire.tsv.gz", source="airr", return_report=True,
)
calls = local.annotate(queries, threads=1)

AIRR junction_aa includes the conserved anchors and is preferred even when cdr3_aa is also present. AIRR cdr3_aa excludes them; a true AIRR table that has only that field is rejected. The reader never fabricates anchors.

Legacy VDJdb cdr3 and legacy producer cdr3_aa can already mean a junction. For an ambiguous custom table, state the convention:

queries = read_rearrangement(
    "custom.csv.gz", source="custom", sequence_convention="junction",
)

Both plain and gzipped CSV/TSV tables are supported. Standard repertoire formats use published vdjtools readers. If a converter collapses or removes source rows so that original identities cannot be preserved, ingestion raises explicitly.

By default, retained source rows keep their order, query_id, supplied sequence_id, V/J calls, counts and pairing linkage. Missing or invalid sequences are counted in the optional report. A supplied count must be a nonnegative integer; malformed, missing, negative and fractional counts raise an error.

To deliberately collapse matching keys, pass dedup=True. The key includes junction, V, J, locus and pair linkage; counts are summed, and query_ids and sequence_ids retain the contributing source identities. This is a different counting unit from the original row table.

Pair chains by a cell identifier#

from vdjmatch.io import read_cell

cells, report = read_cell(
    "cell-rearrangements.tsv", link="cell_id", return_report=True,
)
calls = local.annotate_paired(cells, cdr3a="cdr3a", cdr3b="cdr3b", threads=1)

link can name a custom source column. Every retained chain must have a linkage value and a TRA/TRB locus. Multiple retained alpha or beta rows for a cell raise by default. ambiguity="first" explicitly selects the first row in source order. Missing chains have typed null columns and a descriptive ingestion pair_status.

Paired annotation requires both chains to match the same reference complex with the same peptide–MHC identity. Agreement between two independent chain labels does not establish this paired-reference evidence. Request paired calibration explicitly:

calls = local.annotate_paired(
    cells, cdr3a="cdr3a", cdr3b="cdr3b", calibrate=True,
    species="human", threads=1,
)

For user-supplied paired controls, pass control={"TRA": alpha_index, "TRB": beta_index}; both indexes are required. The returned calibration label paired_independent_fixed_ball states the independent-chain background assumption.

Supply a background explicitly#

from seqtree import Index

controls = {"TRB": Index.build(["CWWWWF", "CYYYYF"], alphabet="aa")}
candidates = local.candidates(queries, control=controls, threads=1)

This tiny background illustrates the interface; use an appropriate versioned background for an application. The API uses uncalibrated ranking unless calibrate=True or a control index is supplied. A locus-to-index mapping allows separate alpha and beta backgrounds. Empty backgrounds and requested background loading failures raise errors.

Hard V/J matching can be requested for uncalibrated annotation with match_v=True or match_j=True. Calibrated requests with these restrictions are rejected because sequence-only control indexes cannot apply the same V/J predicate.

Legacy matching command line#

vdjmatch update --asset default --cache reference-inputs
vdjmatch match --vdjdb reference.zip --input-format airr --threads 1 \
    --no-evalue -o results/run repertoire.tsv
vdjmatch match --vdjdb reference-directory --asset primary \
    --input-format custom --sequence-convention junction --no-evalue \
    -o results/custom custom.csv.gz
vdjmatch match --vdjdb reference.zip --paired --link cell_id \
    --input-format airr -o results/cells cell-rearrangements.tsv

CLI matching requests background calibration by default; --no-evalue selects uncalibrated ranking. The command writes detailed hits, all candidates, calls, descriptive count summaries, ingestion counts and a manifest. See Command-line interface for options and Understand matching evidence before interpreting the summaries.

Run a reproducible CLI comparison#

Use Examples gallery for offline raw-input scripts and two output-free notebooks. The comparison runs fresh annotate --exclude-exact --details invocations with default stem4 and retained advanced vcdr12, then joins CLI tables on source IDs and complete pMHC keys. Use ordinary annotation without --exclude-exact when exact observations belong in the evidence. Choose a new output directory for each run; keep raw inputs and requested final tables, never computed feature caches.