mirpy-lib 4.0.0__tar.gz → 4.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/CHANGELOG.md +60 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/PKG-INFO +28 -13
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/README.md +25 -10
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/signature.rst +21 -1
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/pyproject.toml +2 -2
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/skills/mirpy/SKILL.md +4 -4
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/__init__.py +1 -1
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/cli.py +47 -9
- mirpy_lib-4.2.0/src/mir/resources/signature/corpora.json +92 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/signature/signature.py +43 -24
- mirpy_lib-4.0.0/src/mir/resources/signature/rsig_memory.json +0 -94
- mirpy_lib-4.0.0/src/mir/resources/signature/rsig_memory.npz +0 -0
- mirpy_lib-4.0.0/src/mir/resources/signature/rsig_naive.json +0 -94
- mirpy_lib-4.0.0/src/mir/resources/signature/rsig_naive.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/.gitignore +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/LICENSE +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/assets/mirpy_dark.png +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/assets/mirpy_dark.svg +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/assets/mirpy_dark_bg.svg +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/assets/mirpy_light.png +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/assets/mirpy_light.svg +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/assets/mirpy_light_bg.svg +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/data/signature_reference_studies.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/Makefile +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_ext/themed_graphviz.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_static/custom.css +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_static/depth_robustness.png +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_static/mirpy_dark.png +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_static/mirpy_dark.svg +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_static/mirpy_light.png +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_static/mirpy_light.svg +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_static/sample_embedding_diagram.png +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/_templates/site-nav.html +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/api.rst +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/channels.rst +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/conf.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/examples.rst +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/index.rst +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/math.rst +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/notebooks.rst +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/preprocessing.rst +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/docs/usage.rst +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/setup.sh +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/aliases.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/alleles.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/bench/__init__.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/bench/eval.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/bench/metrics.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/bench/theory.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/bench/vdjdb.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/cohort.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/density.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/distances/__init__.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/distances/germline.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/distances/junction.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/distances/shm.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/embedding/__init__.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/embedding/pca.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/embedding/presets.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/embedding/prototypes.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/embedding/tcremp.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/explain.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/generate.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/__init__.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/bundle.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/codec.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/decoder.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/diffusion.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/encoder.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/set_encoder.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/tokenize.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/ml/train.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/py.typed +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/repertoire.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/__init__.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/gene_library/README.md +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/gene_library/__init__.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/gene_library/build_gene_library.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/gene_library/build_region_annotations.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/gene_library/imgt_gene_library.txt +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/gene_library/olga_gene_library.txt +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/gene_library/region_annotations.txt +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/build_germline_dist.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/human_IGH.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/human_IGK.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/human_IGL.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/human_TRA.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/human_TRB.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/human_TRD.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/human_TRG.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/mouse_IGH.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/mouse_IGK.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/mouse_IGL.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/mouse_TRA.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/mouse_TRB.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/mouse_TRD.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/germline_dist/mouse_TRG.npz +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/.gitkeep +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/generate_prototypes.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/human_IGH.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/human_IGK.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/human_IGL.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/human_TRA.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/human_TRB.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/human_TRD.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/human_TRG.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/manifest.json +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/mouse_TRA.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/prototypes/mouse_TRB.tsv +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/signature/__init__.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/signature/features.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/track.py +0 -0
- {mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/twin.py +0 -0
|
@@ -3,6 +3,66 @@
|
|
|
3
3
|
All notable changes to `mirpy-lib` (import `mir`). This project follows semantic versioning; the v3 line is a
|
|
4
4
|
greenfield ML/embedding rewrite (the classical v1.x/v2 toolkit is frozen on branch `legacy-v2`).
|
|
5
5
|
|
|
6
|
+
## 4.2.0 — 2026-09-28
|
|
7
|
+
|
|
8
|
+
The `rsig` half of the three real corpora, and the mechanism that ships them. Requires vdjtools
|
|
9
|
+
**>=4.2.0**, which owns the fit and the resolver.
|
|
10
|
+
|
|
11
|
+
**Five new corpora**: `blood` (11,117 samples, 947 study_ids), `blood-uncapped` (22,441), `tissue`
|
|
12
|
+
(21,131 / 1,934), `tissue-uncapped` (33,874) and `deep-tcr` (3,936 amplicon samples, TRA + TRB). All
|
|
13
|
+
nine corpora are now fitted at **k=256** per locus, up from 128, and the full rotation ships —
|
|
14
|
+
truncating downward at apply time is exact, so a wider artifact serves every narrower request.
|
|
15
|
+
|
|
16
|
+
**Artifacts are no longer bundled in the wheel.** At k=256 an `rsig` artifact is ~3.1 MB and the pair
|
|
17
|
+
of halves across nine corpora is ~110 MB, so the wheel carries a few KB of `corpora.json` — every
|
|
18
|
+
corpus name with its size and SHA-256 — and the library fetches an artifact on first use into
|
|
19
|
+
`$VDJTOOLS_CORPUS_DIR` (default `~/.cache/vdjtools/signature`), verified against that digest. One
|
|
20
|
+
resolver serves both halves, so they cannot disagree about precedence: a local path always beats a
|
|
21
|
+
download. `mir corpus --fetch all` pre-warms the cache, which is the install-time step.
|
|
22
|
+
|
|
23
|
+
`publish.yml` publishes **only for a `v*` tag** now: without that guard, creating the `corpora-*`
|
|
24
|
+
release fires it and tries to upload whatever version `pyproject` carries.
|
|
25
|
+
|
|
26
|
+
The two halves of a corpus are published by the library that owns each — `vsig` in vdjtools' release
|
|
27
|
+
assets, `rsig` in mirpy's — so neither repo has to know the other's layout, and they share one cache
|
|
28
|
+
directory because a `vsig`/`rsig` pair belongs in one place.
|
|
29
|
+
|
|
30
|
+
A real corpus's manifest carries no cohort label, no dataset name and no accession: the population is
|
|
31
|
+
recorded as rule text with aggregate counts, per-locus read floors and the measured read band. A
|
|
32
|
+
rotation is about features and components.
|
|
33
|
+
|
|
34
|
+
## 4.1.0 — 2026-09-28
|
|
35
|
+
|
|
36
|
+
**Two corpora that describe a real compartment**, `synthetic-blood` and `synthetic-tissue`, shipped
|
|
37
|
+
as `rsig_synthetic-blood.npz` / `rsig_synthetic-tissue.npz` beside the `naive` and `memory` pair.
|
|
38
|
+
Requires vdjtools **>=4.1.0**, which is where the draw lives: one `corpus_plan` resolves a corpus name
|
|
39
|
+
for both halves and one `corpus_meta` writes both manifests, so a `vsig_<name>`/`rsig_<name>` pair
|
|
40
|
+
cannot disagree about the cohort, the three ladders, the rank correlations or the germline it was drawn
|
|
41
|
+
from -- which is what makes joining them on `sample_id` mean anything.
|
|
42
|
+
|
|
43
|
+
Each repertoire is a naive/memory **mixture** drawn across three quantile ladders measured per locus
|
|
44
|
+
on the compartment it is named after: clonotype richness, reads per expanded clone, and the singleton
|
|
45
|
+
fraction that stands in for the naive share. The construction, why reads per *expanded* clone is the
|
|
46
|
+
drawable quantity, and the acceptance table are in [vdjtools'
|
|
47
|
+
CHANGELOG](https://github.com/antigenomics/vdjtools/blob/master/CHANGELOG.md) and
|
|
48
|
+
`docs/signature.rst`. The short version: `naive` and `memory` draw depth over 2.4x-11.0x per locus,
|
|
49
|
+
while real blood TRB richness spans 43x (74 to 3,162 clonotypes, n = 34,365 samples) and tissue IGH
|
|
50
|
+
259x -- and a corpus estimates its bounds, centre and per-PC scaling from its own draw, none of which
|
|
51
|
+
extrapolates.
|
|
52
|
+
|
|
53
|
+
### Changed: `synthesize` takes a corpus name, and the rsig manifest records its depth
|
|
54
|
+
|
|
55
|
+
`mir.signature.synthesize`'s first parameter is `corpus_name` (`naive` | `memory` |
|
|
56
|
+
`synthetic-blood` | `synthetic-tissue`), not `regime`. `mir corpus --corpus` accepts all four, and
|
|
57
|
+
`--size` defaults to `auto` -- 10,000 for a pure regime, the cohort's median richness per locus for a
|
|
58
|
+
`synthetic-*` one. `--depth-spread` is new and is **refused** on a `synthetic-*` corpus rather than
|
|
59
|
+
silently ignored, because that band is measured.
|
|
60
|
+
|
|
61
|
+
The rsig manifest now carries `depth_spread`, `depth_spread_requested`, `cohort`, `richness_band`,
|
|
62
|
+
`expanded_count_band`, `singleton_frac_band` and `rank_corr`. It previously carried **no depth field at
|
|
63
|
+
all**, which left the one thing a reader needs in order to know whether a corpus covers their samples
|
|
64
|
+
readable only from the vsig half.
|
|
65
|
+
|
|
6
66
|
## 4.0.0 — 2026-09-27
|
|
7
67
|
|
|
8
68
|
**Signatures rewritten from scratch.** The geometry half; the statistics half is vdjtools, which
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mirpy-lib
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: ML-oriented embeddings for immune receptor repertoires (TCR/BCR)
|
|
5
5
|
Project-URL: Homepage, https://github.com/antigenomics/mirpy
|
|
6
6
|
Project-URL: Repository, https://github.com/antigenomics/mirpy
|
|
@@ -24,7 +24,7 @@ Requires-Dist: polars
|
|
|
24
24
|
Requires-Dist: scikit-learn
|
|
25
25
|
Requires-Dist: scipy
|
|
26
26
|
Requires-Dist: seqtree>=1.0.0
|
|
27
|
-
Requires-Dist: vdjtools>=4.0
|
|
27
|
+
Requires-Dist: vdjtools>=4.2.0
|
|
28
28
|
Provides-Extra: ann
|
|
29
29
|
Requires-Dist: numba>=0.60; extra == 'ann'
|
|
30
30
|
Requires-Dist: pynndescent; extra == 'ann'
|
|
@@ -38,7 +38,7 @@ Requires-Dist: matplotlib; extra == 'bench'
|
|
|
38
38
|
Requires-Dist: seaborn; extra == 'bench'
|
|
39
39
|
Provides-Extra: build
|
|
40
40
|
Requires-Dist: biopython; extra == 'build'
|
|
41
|
-
Requires-Dist: vdjtools[model]>=4.0
|
|
41
|
+
Requires-Dist: vdjtools[model]>=4.2.0; extra == 'build'
|
|
42
42
|
Provides-Extra: dev
|
|
43
43
|
Requires-Dist: psutil>=5; extra == 'dev'
|
|
44
44
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
@@ -145,9 +145,9 @@ mir embed repertoires cohort/*.tsv.gz -o phi.tsv --mmd mmd.tsv
|
|
|
145
145
|
|
|
146
146
|
# the portable signature -> one fixed, named, standardised feature vector per sample
|
|
147
147
|
# One tool per half: mirpy emits the geometry, vdjtools the statistics. Join on sample_id.
|
|
148
|
-
mir
|
|
149
|
-
mir signature --corpus
|
|
150
|
-
mir
|
|
148
|
+
mir signature --corpus blood cohort/*.tsv.gz -o rsig.parquet # fetched on first use
|
|
149
|
+
mir signature --corpus blood --components 32 --describe # exactly what you get
|
|
150
|
+
mir corpus --fetch all # pre-warm the corpus cache
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
`mir embed clonotypes -h` / `mir embed repertoires -h` list every flag (species, locus,
|
|
@@ -503,17 +503,30 @@ regression, boosting or an MLP with no scaler of their own.
|
|
|
503
503
|
Command line and library both, and they emit the same columns:
|
|
504
504
|
|
|
505
505
|
```bash
|
|
506
|
-
mir
|
|
507
|
-
mir
|
|
506
|
+
mir signature --corpus synthetic-blood cohort/*.tsv.gz -o rsig.parquet
|
|
507
|
+
mir corpus --corpus synthetic-tissue -o rsig_synthetic-tissue.npz # or build your own
|
|
508
508
|
```
|
|
509
509
|
|
|
510
510
|
```python
|
|
511
511
|
from mir.signature import Corpus, rsig_cohort
|
|
512
512
|
|
|
513
|
-
corpus = Corpus.load("
|
|
514
|
-
F = rsig_cohort(samples, corpus, n_jobs=0)
|
|
513
|
+
corpus = Corpus.load("rsig_synthetic-blood.npz") # REQUIRED -- there is no default
|
|
514
|
+
F = rsig_cohort(samples, corpus, n_jobs=0) # one row per sample
|
|
515
515
|
```
|
|
516
516
|
|
|
517
|
+
**Nine corpora, all at 256 components per locus.** Three are fitted on real repertoires -- `blood`
|
|
518
|
+
(11,117 bulk RNA-seq blood samples, 947 study groups), `tissue` (21,131 / 1,934) and `deep-tcr` (3,936
|
|
519
|
+
amplicon samples, TRA+TRB) -- each with an uncapped variant so the per-study cap's effect is
|
|
520
|
+
measurable. `synthetic-blood` and `synthetic-tissue` draw each repertoire as a naive/memory mixture
|
|
521
|
+
across three quantile ladders measured per locus on that compartment (richness, reads per expanded
|
|
522
|
+
clone, and the singleton fraction that stands in for the naive share), so they span the real depth
|
|
523
|
+
range while needing no cohort to rebuild; `naive` and `memory` remain the pure-regime references.
|
|
524
|
+
|
|
525
|
+
Artifacts are **fetched on first use** rather than bundled: at k=256 the nine corpora are ~110 MB
|
|
526
|
+
across both halves, so the wheel carries a few KB of index and downloads what you name into
|
|
527
|
+
`~/.cache/vdjtools/signature`, verified against the shipped SHA-256. `mir corpus --fetch all`
|
|
528
|
+
pre-warms it.
|
|
529
|
+
|
|
517
530
|
Two halves, joined on `sample_id` and namespaced so they never collide: `vsig` (statistics of the
|
|
518
531
|
clone-size vector, from [vdjtools](https://github.com/antigenomics/vdjtools)) and `rsig` (geometry —
|
|
519
532
|
functionals of `Φ`). A column is `<sig>:<block>:<locus>:<feature>`. What comes out per locus is
|
|
@@ -532,9 +545,11 @@ and its centre and scale were fitted on zero rows of that artifact and arrived f
|
|
|
532
545
|
corpus. Two independent fits, stitched; one shipped reference paired a centre of exactly `0.0` with
|
|
533
546
|
a scale plainly fitted from data and put a corpus-typical sample **81 robust deviations** out.
|
|
534
547
|
|
|
535
|
-
|
|
536
|
-
library — `
|
|
537
|
-
|
|
548
|
+
All four shipped corpora need no cohort at all and are reproducible by anyone who installs the
|
|
549
|
+
library — `synthetic-blood` and `synthetic-tissue` (the naive/memory mixture drawn across a
|
|
550
|
+
compartment's measured per-locus ladders), plus the pure regimes `naive` (every clone size 1, what
|
|
551
|
+
the recombination model emits) and `memory` (Zipf rank-abundance clone sizes). `deep-tcr`, `blood`
|
|
552
|
+
and `tissue` will be fitted on real cohorts and are not shipped yet.
|
|
538
553
|
|
|
539
554
|
**Holes are never zeros.** An unsequenced locus, a compartment below its clonotype floor, or a
|
|
540
555
|
statistic the sample is too shallow to estimate is `nan` plus a `mask:` column, because a model that
|
|
@@ -89,9 +89,9 @@ mir embed repertoires cohort/*.tsv.gz -o phi.tsv --mmd mmd.tsv
|
|
|
89
89
|
|
|
90
90
|
# the portable signature -> one fixed, named, standardised feature vector per sample
|
|
91
91
|
# One tool per half: mirpy emits the geometry, vdjtools the statistics. Join on sample_id.
|
|
92
|
-
mir
|
|
93
|
-
mir signature --corpus
|
|
94
|
-
mir
|
|
92
|
+
mir signature --corpus blood cohort/*.tsv.gz -o rsig.parquet # fetched on first use
|
|
93
|
+
mir signature --corpus blood --components 32 --describe # exactly what you get
|
|
94
|
+
mir corpus --fetch all # pre-warm the corpus cache
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
`mir embed clonotypes -h` / `mir embed repertoires -h` list every flag (species, locus,
|
|
@@ -447,17 +447,30 @@ regression, boosting or an MLP with no scaler of their own.
|
|
|
447
447
|
Command line and library both, and they emit the same columns:
|
|
448
448
|
|
|
449
449
|
```bash
|
|
450
|
-
mir
|
|
451
|
-
mir
|
|
450
|
+
mir signature --corpus synthetic-blood cohort/*.tsv.gz -o rsig.parquet
|
|
451
|
+
mir corpus --corpus synthetic-tissue -o rsig_synthetic-tissue.npz # or build your own
|
|
452
452
|
```
|
|
453
453
|
|
|
454
454
|
```python
|
|
455
455
|
from mir.signature import Corpus, rsig_cohort
|
|
456
456
|
|
|
457
|
-
corpus = Corpus.load("
|
|
458
|
-
F = rsig_cohort(samples, corpus, n_jobs=0)
|
|
457
|
+
corpus = Corpus.load("rsig_synthetic-blood.npz") # REQUIRED -- there is no default
|
|
458
|
+
F = rsig_cohort(samples, corpus, n_jobs=0) # one row per sample
|
|
459
459
|
```
|
|
460
460
|
|
|
461
|
+
**Nine corpora, all at 256 components per locus.** Three are fitted on real repertoires -- `blood`
|
|
462
|
+
(11,117 bulk RNA-seq blood samples, 947 study groups), `tissue` (21,131 / 1,934) and `deep-tcr` (3,936
|
|
463
|
+
amplicon samples, TRA+TRB) -- each with an uncapped variant so the per-study cap's effect is
|
|
464
|
+
measurable. `synthetic-blood` and `synthetic-tissue` draw each repertoire as a naive/memory mixture
|
|
465
|
+
across three quantile ladders measured per locus on that compartment (richness, reads per expanded
|
|
466
|
+
clone, and the singleton fraction that stands in for the naive share), so they span the real depth
|
|
467
|
+
range while needing no cohort to rebuild; `naive` and `memory` remain the pure-regime references.
|
|
468
|
+
|
|
469
|
+
Artifacts are **fetched on first use** rather than bundled: at k=256 the nine corpora are ~110 MB
|
|
470
|
+
across both halves, so the wheel carries a few KB of index and downloads what you name into
|
|
471
|
+
`~/.cache/vdjtools/signature`, verified against the shipped SHA-256. `mir corpus --fetch all`
|
|
472
|
+
pre-warms it.
|
|
473
|
+
|
|
461
474
|
Two halves, joined on `sample_id` and namespaced so they never collide: `vsig` (statistics of the
|
|
462
475
|
clone-size vector, from [vdjtools](https://github.com/antigenomics/vdjtools)) and `rsig` (geometry —
|
|
463
476
|
functionals of `Φ`). A column is `<sig>:<block>:<locus>:<feature>`. What comes out per locus is
|
|
@@ -476,9 +489,11 @@ and its centre and scale were fitted on zero rows of that artifact and arrived f
|
|
|
476
489
|
corpus. Two independent fits, stitched; one shipped reference paired a centre of exactly `0.0` with
|
|
477
490
|
a scale plainly fitted from data and put a corpus-typical sample **81 robust deviations** out.
|
|
478
491
|
|
|
479
|
-
|
|
480
|
-
library — `
|
|
481
|
-
|
|
492
|
+
All four shipped corpora need no cohort at all and are reproducible by anyone who installs the
|
|
493
|
+
library — `synthetic-blood` and `synthetic-tissue` (the naive/memory mixture drawn across a
|
|
494
|
+
compartment's measured per-locus ladders), plus the pure regimes `naive` (every clone size 1, what
|
|
495
|
+
the recombination model emits) and `memory` (Zipf rank-abundance clone sizes). `deep-tcr`, `blood`
|
|
496
|
+
and `tissue` will be fitted on real cohorts and are not shipped yet.
|
|
482
497
|
|
|
483
498
|
**Holes are never zeros.** An unsequenced locus, a compartment below its clonotype floor, or a
|
|
484
499
|
statistic the sample is too shallow to estimate is `nan` plus a `mask:` column, because a model that
|
|
@@ -24,7 +24,10 @@ Quickstart
|
|
|
24
24
|
|
|
25
25
|
.. code-block:: bash
|
|
26
26
|
|
|
27
|
-
#
|
|
27
|
+
# four corpora ship with the wheel; name one, no build and no cohort needed
|
|
28
|
+
mir signature --corpus synthetic-blood cohort/*.tsv.gz -o rsig.tsv
|
|
29
|
+
|
|
30
|
+
# or build your own -- still uses no samples from anybody's cohort
|
|
28
31
|
mir corpus --corpus naive --smoke -o naive_rsig.npz
|
|
29
32
|
|
|
30
33
|
mir signature --corpus naive_rsig.npz cohort/*.tsv.gz -o rsig.tsv
|
|
@@ -187,11 +190,28 @@ Building a corpus
|
|
|
187
190
|
|
|
188
191
|
.. code-block:: bash
|
|
189
192
|
|
|
193
|
+
mir corpus --corpus synthetic-blood -o synthetic-blood_rsig.npz
|
|
194
|
+
mir corpus --corpus synthetic-tissue -o synthetic-tissue_rsig.npz
|
|
190
195
|
mir corpus --corpus naive -o naive_rsig.npz
|
|
191
196
|
mir corpus --corpus memory --size n_eff --components 0.95 -o memory_rsig.npz
|
|
192
197
|
mir corpus --smoke -o /tmp/smoke.npz
|
|
193
198
|
mir corpus --corpus naive -j 8 -o naive_rsig.npz # 8 worker processes
|
|
194
199
|
|
|
200
|
+
Four corpora ship, all synthetic. ``synthetic-blood`` and ``synthetic-tissue`` are the ones to reach
|
|
201
|
+
for: each repertoire is a naive/memory **mixture** drawn from three quantile ladders measured per
|
|
202
|
+
locus on the cohort it is named after -- richness, reads per expanded clone, and the singleton
|
|
203
|
+
fraction that stands in for the naive compartment -- through that cohort's measured rank
|
|
204
|
+
correlations. ``naive`` and ``memory`` are the pure regimes, and neither varies what a cohort varies:
|
|
205
|
+
``memory`` at a fixed size has the same read count in every sample. The construction, the
|
|
206
|
+
acceptance table and why reads per *expanded* clone is the drawable quantity are in `the vdjtools
|
|
207
|
+
half <https://docs.isalgo.dev/vdjtools/signature.html#the-two-cohort-corpora-three-measured-ladders-per-locus>`_,
|
|
208
|
+
since one module draws for both.
|
|
209
|
+
|
|
210
|
+
Both halves must be built from the **same corpus name and seed**: they resolve the name through one
|
|
211
|
+
``corpus_plan`` and write the manifest through one ``corpus_meta``, so the cohort, the three ladders,
|
|
212
|
+
the rank correlations and the germline fingerprint cannot disagree between ``vsig_<name>`` and
|
|
213
|
+
``rsig_<name>`` -- which is what makes joining them on ``sample_id`` meaningful.
|
|
214
|
+
|
|
195
215
|
The build runs across worker **processes**, one contiguous block of samples each; ``-j/--jobs``
|
|
196
216
|
defaults to every core the process may use (``-j 1`` stays in-process). Both halves share one
|
|
197
217
|
builder, so a sample is the same repertoire in either -- its generator is seeded from
|
|
@@ -36,7 +36,7 @@ dependencies = [
|
|
|
36
36
|
# `vdjtools.signature.corpus`, which does not exist before 4.0.0, and 4.0.0 never
|
|
37
37
|
# reached PyPI. On the old `>=3.18.0` pip would have resolved vdjtools 3.18.1 beside
|
|
38
38
|
# mirpy 4.0.0 and every signature entry point would have failed on import.
|
|
39
|
-
"vdjtools>=4.0
|
|
39
|
+
"vdjtools>=4.2.0",
|
|
40
40
|
]
|
|
41
41
|
license = { text = "GPL-3.0-or-later" }
|
|
42
42
|
|
|
@@ -46,7 +46,7 @@ annotate = ["vdjmatch"]
|
|
|
46
46
|
# Build-time only: regenerate region_annotations.txt (arda) and the baked
|
|
47
47
|
# germline distance matrices (BioPython). Runtime needs neither. arda comes
|
|
48
48
|
# via vdjtools[model] (single arda version across the ecosystem), not pinned directly.
|
|
49
|
-
build = ["vdjtools[model]>=4.0
|
|
49
|
+
build = ["vdjtools[model]>=4.2.0", "biopython"]
|
|
50
50
|
# Part 2: neural codecs and density methods.
|
|
51
51
|
ml = ["torch"]
|
|
52
52
|
# Benchmark harness: DBSCAN eps selection + plotting + HuggingFace dataset fetch +
|
|
@@ -63,11 +63,11 @@ pip install "mirpy-lib[build]" # BioPython + arda: regenerate baked r
|
|
|
63
63
|
```bash
|
|
64
64
|
mir embed clonotypes SAMPLE -o out.parquet # per-clonotype table (e0…)
|
|
65
65
|
mir embed repertoires S1 S2 … -o phi.tsv --mmd mmd.tsv # one Φ(S) per sample per chain (phi0…)
|
|
66
|
-
mir corpus --
|
|
67
|
-
mir signature S1 S2 … --corpus
|
|
66
|
+
mir corpus --fetch all # pre-warm the corpus cache
|
|
67
|
+
mir signature S1 S2 … --corpus blood -o rsig.parquet # geometry ONLY (rsig)
|
|
68
68
|
# the vsig half is `vdjtools signature`, against the SAME corpus name and seed;
|
|
69
69
|
# join the two on sample_id for the full vector
|
|
70
|
-
mir signature --corpus
|
|
70
|
+
mir signature --corpus blood --describe # exactly what this invocation emits
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
- Reads anything `vdjtools.io.read` sniffs (AIRR TSV, vdjtools, MiXCR, immunoSEQ, parquet).
|
|
@@ -153,7 +153,7 @@ one contract: `vsig` (statistics, from `vdjtools.signature`) and `rsig` (geometr
|
|
|
153
153
|
|
|
154
154
|
```python
|
|
155
155
|
from mir.signature import Corpus, rsig, rsig_cohort, raw_and_channels, synthesize
|
|
156
|
-
corpus = Corpus.load("
|
|
156
|
+
corpus = Corpus.load(bundled_path("blood")) # REQUIRED -- no default corpus
|
|
157
157
|
v = rsig({"TRB": df}, corpus)
|
|
158
158
|
F = rsig_cohort(samples, corpus, n_jobs=0)
|
|
159
159
|
raw, chan = raw_and_channels({"TRB": df}, corpus.vocab) # un-rotated, natural units
|
|
@@ -407,12 +407,30 @@ def cmd_corpus(a: argparse.Namespace) -> None:
|
|
|
407
407
|
|
|
408
408
|
from mir.signature import synthesize
|
|
409
409
|
|
|
410
|
-
|
|
411
|
-
|
|
410
|
+
if a.fetch:
|
|
411
|
+
from vdjtools.signature.corpus import corpora_index, corpus_cache_dir, fetch_artifact
|
|
412
|
+
|
|
413
|
+
from mir.signature.signature import CORPUS_REPO, _RES, bundled_names
|
|
414
|
+
idx = corpora_index(_RES)
|
|
415
|
+
if not idx:
|
|
416
|
+
raise SystemExit("this version ships no corpus index, so there is nothing to fetch; "
|
|
417
|
+
f"the corpora it knows are {', '.join(bundled_names()) or '(none)'}")
|
|
418
|
+
for nm in (sorted(idx) if a.fetch == "all" else [a.fetch]):
|
|
419
|
+
if nm not in idx:
|
|
420
|
+
raise SystemExit(f"no published corpus named {nm!r}; have {', '.join(sorted(idx))}")
|
|
421
|
+
p = fetch_artifact(nm, sig="rsig", res_dir=_RES, repo=CORPUS_REPO)
|
|
422
|
+
print(f"[mir] {p} {p.stat().st_size / 1e6:.2f} MB", file=sys.stderr)
|
|
423
|
+
print(f"[mir] cache: {corpus_cache_dir()}", file=sys.stderr)
|
|
424
|
+
return
|
|
425
|
+
if not a.output:
|
|
426
|
+
raise SystemExit("-o/--output is required when building a corpus (--fetch does not use it)")
|
|
427
|
+
|
|
428
|
+
size = None if a.size == "auto" else a.size
|
|
429
|
+
if a.size not in ("auto", "n_eff", "p05", "p95"):
|
|
412
430
|
try:
|
|
413
|
-
size = int(size)
|
|
431
|
+
size = int(a.size)
|
|
414
432
|
except ValueError:
|
|
415
|
-
raise SystemExit(f"--size must be an integer or one of n_eff, p05, p95; "
|
|
433
|
+
raise SystemExit(f"--size must be an integer or one of auto, n_eff, p05, p95; "
|
|
416
434
|
f"got {a.size!r}") from None
|
|
417
435
|
n_samples = a.samples
|
|
418
436
|
if a.smoke:
|
|
@@ -424,6 +442,7 @@ def cmd_corpus(a: argparse.Namespace) -> None:
|
|
|
424
442
|
art, _rows = synthesize(a.corpus, n_samples=n_samples, size=size, seed=a.seed,
|
|
425
443
|
n_components=ks, mode=a.winsorize, winsor_p=a.winsor_p,
|
|
426
444
|
source=a.source, species=a.species, n_jobs=a.jobs,
|
|
445
|
+
depth_spread=a.depth_spread,
|
|
427
446
|
progress=lambda loc, d, t: print(
|
|
428
447
|
f" {loc:4s} {d}/{t} {time.time() - t0:5.0f}s",
|
|
429
448
|
file=sys.stderr, flush=True), **kw)
|
|
@@ -552,6 +571,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
552
571
|
"installs the library.\n"),
|
|
553
572
|
epilog=(
|
|
554
573
|
" mir corpus --corpus naive -o naive.npz\n"
|
|
574
|
+
" mir corpus --corpus synthetic-blood -o synthetic-blood.npz\n"
|
|
555
575
|
" mir corpus --corpus memory --size n_eff --components 0.95 -o memory.npz\n"
|
|
556
576
|
" mir corpus --smoke -o /tmp/smoke.npz # minutes, not hours\n"
|
|
557
577
|
"\n"
|
|
@@ -562,12 +582,24 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
562
582
|
"Use the SAME name and seed as `vdjtools corpus` so the two halves describe the\n"
|
|
563
583
|
"same repertoires; that is what makes joining them meaningful.\n"),
|
|
564
584
|
)
|
|
565
|
-
c2.add_argument("--corpus", default="naive",
|
|
566
|
-
|
|
567
|
-
|
|
585
|
+
c2.add_argument("--corpus", default="naive",
|
|
586
|
+
choices=("naive", "memory", "synthetic-blood", "synthetic-tissue"),
|
|
587
|
+
help="naive: every clone size 1; memory: Zipf rank-abundance clone sizes; "
|
|
588
|
+
"synthetic-blood / synthetic-tissue: the mixture of the two, drawn across "
|
|
589
|
+
"that real cohort's measured per-locus richness, reads per clonotype and "
|
|
590
|
+
"singleton fraction")
|
|
591
|
+
c2.add_argument("-o", "--output", help="artifact path; writes .npz and .json. Required "
|
|
592
|
+
"unless --fetch, which downloads into the cache instead")
|
|
593
|
+
c2.add_argument("--fetch", metavar="NAME|all",
|
|
594
|
+
help="download a published corpus instead of building one, into the cache "
|
|
595
|
+
"($VDJTOOLS_CORPUS_DIR or ~/.cache/vdjtools/signature); `all` pre-warms "
|
|
596
|
+
"every corpus, which is the install-time step since an artifact is "
|
|
597
|
+
"otherwise fetched on first use")
|
|
568
598
|
c2.add_argument("--samples", type=int, default=10_000, help="repertoires in the corpus")
|
|
569
|
-
c2.add_argument("--size", default="
|
|
570
|
-
help="receptors per repertoire: an integer, or n_eff / p05 / p95"
|
|
599
|
+
c2.add_argument("--size", default="auto",
|
|
600
|
+
help="receptors per repertoire: an integer, or n_eff / p05 / p95. Default auto "
|
|
601
|
+
"is the corpus's own -- 10000 for naive/memory, the centre of the cohort's "
|
|
602
|
+
"measured richness band for a synthetic-* one")
|
|
571
603
|
c2.add_argument("--components", default="128",
|
|
572
604
|
help="components per locus: an integer count, or a variance fraction")
|
|
573
605
|
c2.add_argument("--winsorize", default="features", choices=("features", "pcs", "none"))
|
|
@@ -576,6 +608,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
576
608
|
c2.add_argument("--loci", default=None, help="comma-separated subset (default: all seven)")
|
|
577
609
|
c2.add_argument("--source", default="olga", choices=("olga", "learned", "arda"))
|
|
578
610
|
c2.add_argument("--species", default="human")
|
|
611
|
+
c2.add_argument("--depth-spread", type=float, default=None,
|
|
612
|
+
help="multiplicative depth range each repertoire's size is drawn log-uniformly "
|
|
613
|
+
"across, around --size; default is the corpus's own (2.4x-11.0x for "
|
|
614
|
+
"naive/memory, 43x on blood TRB and 259x on tissue IGH for the "
|
|
615
|
+
"synthetic-* ones). --size 3162 --depth-spread 1000 spans 100 to 100000 "
|
|
616
|
+
"receptors. Must match the vsig half to describe the same repertoires")
|
|
579
617
|
c2.add_argument("--smoke", action="store_true",
|
|
580
618
|
help="reduced build (200 samples of 1000) for tests and the cmp check")
|
|
581
619
|
c2.add_argument("-j", "--jobs", type=int, default=0,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"blood": {
|
|
3
|
+
"json": {
|
|
4
|
+
"bytes": 2522,
|
|
5
|
+
"sha256": "366d788ec0ad0866eaf29ab9f9595daf33699b0db0b8e2a6e38a25f72b3caeeb"
|
|
6
|
+
},
|
|
7
|
+
"npz": {
|
|
8
|
+
"bytes": 3106113,
|
|
9
|
+
"sha256": "eed16130f1a49a3adce5d06cd58f94908ad872834c652aabd1ef52230cca1dc1"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"blood-uncapped": {
|
|
13
|
+
"json": {
|
|
14
|
+
"bytes": 2542,
|
|
15
|
+
"sha256": "527dc2f0a5bd659ea6ce4a545be6629300d0a0f4c8247e8910e30393900c1c51"
|
|
16
|
+
},
|
|
17
|
+
"npz": {
|
|
18
|
+
"bytes": 3106878,
|
|
19
|
+
"sha256": "7c647d147d82a3bf1d4ac1545014976367fb2b6848e1dfda48c62266f5a3cbd0"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"deep-tcr": {
|
|
23
|
+
"json": {
|
|
24
|
+
"bytes": 1662,
|
|
25
|
+
"sha256": "dfe85f918778f1d4dcc0702bbb225aba9fc8418aa44874ab6e7db4bb2162c9af"
|
|
26
|
+
},
|
|
27
|
+
"npz": {
|
|
28
|
+
"bytes": 1008863,
|
|
29
|
+
"sha256": "68e102627f2d9db454520321fa3b8c253bd2909bfbec8d6395959003f9535760"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"memory": {
|
|
33
|
+
"json": {
|
|
34
|
+
"bytes": 2247,
|
|
35
|
+
"sha256": "2aea36575627e83dd991cbace08efbccd72f27ff8d65d32a952ec2bbc03d102c"
|
|
36
|
+
},
|
|
37
|
+
"npz": {
|
|
38
|
+
"bytes": 3103450,
|
|
39
|
+
"sha256": "45c77bdf918b55e4a21044246dfbb205a4e65e3ab7564a68de16216f9b175df3"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"naive": {
|
|
43
|
+
"json": {
|
|
44
|
+
"bytes": 2241,
|
|
45
|
+
"sha256": "8127ff977fd5ede5ba78471ac4ae305f3fe22852960c6ebd2da233b1e2558f33"
|
|
46
|
+
},
|
|
47
|
+
"npz": {
|
|
48
|
+
"bytes": 3101749,
|
|
49
|
+
"sha256": "007fbc2fc1c9f4228ef88aec2bd690a0294218e74dfff93fc472ee6caa5613e5"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"synthetic-blood": {
|
|
53
|
+
"json": {
|
|
54
|
+
"bytes": 3812,
|
|
55
|
+
"sha256": "9560ddd0d6fff1a3fcb345153f1e0cf17b99a45a44681124481c508f164845f6"
|
|
56
|
+
},
|
|
57
|
+
"npz": {
|
|
58
|
+
"bytes": 3102697,
|
|
59
|
+
"sha256": "fd3722027de7957b69dc8a9e0a35e548c94de9aff5e441ccc10ccd28083fad68"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"synthetic-tissue": {
|
|
63
|
+
"json": {
|
|
64
|
+
"bytes": 3805,
|
|
65
|
+
"sha256": "4c7f12a1313c9f52aec2f61e11cde0efaf91e25e61304aab353b0542d9e270e9"
|
|
66
|
+
},
|
|
67
|
+
"npz": {
|
|
68
|
+
"bytes": 3102745,
|
|
69
|
+
"sha256": "bf66da96d8a778d9a626d1dfa1eb738b50869034fa248404d270dd54ddf3ab8d"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"tissue": {
|
|
73
|
+
"json": {
|
|
74
|
+
"bytes": 2520,
|
|
75
|
+
"sha256": "8936971ddf6d12bb39f9a4bcd9def6b2c9e075811fc044636760102effcacfd5"
|
|
76
|
+
},
|
|
77
|
+
"npz": {
|
|
78
|
+
"bytes": 3105251,
|
|
79
|
+
"sha256": "531e814ecdd053327adc3c4baf9cf81e5e73b80deadc77b89dd0098d82dd8eee"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"tissue-uncapped": {
|
|
83
|
+
"json": {
|
|
84
|
+
"bytes": 2538,
|
|
85
|
+
"sha256": "983d38cd3e597e3698528ded9a15ca6665199fee9122da7a11c0926e9ba7644b"
|
|
86
|
+
},
|
|
87
|
+
"npz": {
|
|
88
|
+
"bytes": 3105365,
|
|
89
|
+
"sha256": "aa82dae5dcf149292061caad4d1744f2665c3cf482f454fe4ac1e72fa4f4e636"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -63,24 +63,33 @@ _register()
|
|
|
63
63
|
_RES = __import__("pathlib").Path(__file__).resolve().parent.parent / "resources" / "signature"
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
#: The repo whose release assets carry the rsig artifacts. The vsig half lives in vdjtools' releases;
|
|
67
|
+
#: each half is published by the library that owns it, so a corpus name resolves to two downloads from
|
|
68
|
+
#: two repos rather than one repo having to know about the other's layout.
|
|
69
|
+
CORPUS_REPO = "antigenomics/mirpy"
|
|
70
|
+
|
|
71
|
+
|
|
66
72
|
def bundled_names() -> list[str]:
|
|
67
|
-
"""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
"""Every corpus name this version knows -- installed, cached, or downloadable."""
|
|
74
|
+
from vdjtools.signature.corpus import corpora_index, corpus_cache_dir
|
|
75
|
+
|
|
76
|
+
got = {p.stem.removeprefix("rsig_") for p in _RES.glob("rsig_*.npz")} if _RES.is_dir() else set()
|
|
77
|
+
cache = corpus_cache_dir()
|
|
78
|
+
if cache.is_dir():
|
|
79
|
+
got |= {p.stem.removeprefix("rsig_") for p in cache.glob("rsig_*.npz")}
|
|
80
|
+
return sorted(got | set(corpora_index(_RES)))
|
|
71
81
|
|
|
72
82
|
|
|
73
83
|
def bundled_path(name):
|
|
74
|
-
"""Resolve a
|
|
75
|
-
|
|
84
|
+
"""Resolve a corpus name or a filesystem path to an ``rsig`` artifact, else ``None``.
|
|
85
|
+
|
|
86
|
+
Downloads on first use if the name is in the shipped index and not yet cached. One resolver,
|
|
87
|
+
shared with the vsig half, so the two cannot disagree about precedence -- a local path always
|
|
88
|
+
wins over a download.
|
|
89
|
+
"""
|
|
90
|
+
from vdjtools.signature.corpus import resolve_artifact
|
|
76
91
|
|
|
77
|
-
|
|
78
|
-
if direct.suffix == ".npz" and direct.exists():
|
|
79
|
-
return direct
|
|
80
|
-
if direct.with_suffix(".npz").exists():
|
|
81
|
-
return direct.with_suffix(".npz")
|
|
82
|
-
cand = _RES / f"rsig_{name}.npz"
|
|
83
|
-
return cand if cand.exists() else None
|
|
92
|
+
return resolve_artifact(name, sig="rsig", res_dir=_RES, repo=CORPUS_REPO)
|
|
84
93
|
|
|
85
94
|
|
|
86
95
|
def _model(species: str, locus: str):
|
|
@@ -261,11 +270,11 @@ def rsig_cohort(samples, corpus: C.Corpus, *, n_jobs: int = 1,
|
|
|
261
270
|
return pl.DataFrame(rows).select([c for c in want if any(c in r for r in rows)])
|
|
262
271
|
|
|
263
272
|
|
|
264
|
-
def synthesize(
|
|
265
|
-
size: "int | str" =
|
|
273
|
+
def synthesize(corpus_name: str, *, loci: "tuple[str, ...]" = L.LOCI, n_samples: int = 10_000,
|
|
274
|
+
size: "int | str | None" = None, seed: int = C.SEED,
|
|
266
275
|
n_components: "int | float" = C.DEFAULT_COMPONENTS, mode: str = "features",
|
|
267
276
|
winsor_p: float = 0.01, source: str = "olga", species: str = "human",
|
|
268
|
-
progress=None, n_jobs: int = 1):
|
|
277
|
+
progress=None, n_jobs: int = 1, depth_spread: "float | str | None" = None):
|
|
269
278
|
"""Build and fit an ``rsig`` corpus on the same synthetic repertoires ``vsig`` uses.
|
|
270
279
|
|
|
271
280
|
The receptors, the seeds and the drawn depths are all
|
|
@@ -274,8 +283,17 @@ def synthesize(regime: str, *, loci: "tuple[str, ...]" = L.LOCI, n_samples: int
|
|
|
274
283
|
``sample_id`` meaningful rather than merely type-correct.
|
|
275
284
|
|
|
276
285
|
Args:
|
|
286
|
+
corpus_name: One of :data:`vdjtools.signature.corpus.SYNTHETIC` -- ``"naive"`` / ``"memory"``
|
|
287
|
+
for the pure regimes, ``"synthetic-blood"`` / ``"synthetic-tissue"`` for the naive/memory
|
|
288
|
+
mixture drawn across that cohort's measured per-locus richness, read-depth and
|
|
289
|
+
singleton-fraction bands.
|
|
277
290
|
n_jobs: Worker **processes** (not kernel threads). ``0`` means every available core; ``1``,
|
|
278
291
|
the default, runs in-process. The artifact is bit-identical at every value.
|
|
292
|
+
size: ``None`` is the corpus's own -- 10,000 for a pure regime, the geometric centre of the
|
|
293
|
+
cohort's measured richness band for a ``synthetic-*`` one.
|
|
294
|
+
depth_spread: Multiplicative depth range each repertoire's size is drawn log-uniformly
|
|
295
|
+
across, around ``size``; ``None`` is the corpus's own. Must match the ``vsig`` half's
|
|
296
|
+
value for the two to describe the same repertoires.
|
|
279
297
|
|
|
280
298
|
Returns:
|
|
281
299
|
``(corpus, mats)`` -- ``mats`` being ``{locus: (matrix, columns)}``, the corpus matrix itself.
|
|
@@ -284,23 +302,24 @@ def synthesize(regime: str, *, loci: "tuple[str, ...]" = L.LOCI, n_samples: int
|
|
|
284
302
|
|
|
285
303
|
import mir
|
|
286
304
|
|
|
305
|
+
regime, cohort, size, depth_spread = C.corpus_plan(corpus_name, size=size,
|
|
306
|
+
depth_spread=depth_spread)
|
|
287
307
|
vocab = {loc: {} for loc in loci}
|
|
288
308
|
# Parallel across samples, in the one shared builder: an rsig row is a pure function of its
|
|
289
309
|
# sample, so a worker draws its own repertoires from the memory-mapped pools and only the row of
|
|
290
310
|
# numbers comes back. ``n_jobs`` does not change a single value.
|
|
291
311
|
mats = C.build_matrices(
|
|
292
312
|
regime, sig="rsig", vocab=vocab, loci=loci, n_samples=n_samples, size=size, seed=seed,
|
|
293
|
-
source=source, n_jobs=n_jobs, progress=progress,
|
|
313
|
+
source=source, n_jobs=n_jobs, progress=progress, depth_spread=depth_spread, cohort=cohort,
|
|
294
314
|
featurise=partial(raw_and_channels, vocab=vocab, species=species))
|
|
295
315
|
return C.fit_matrices(
|
|
296
|
-
mats, vocab, sig="rsig", name=
|
|
316
|
+
mats, vocab, sig="rsig", name=corpus_name, mode=mode, n_components=n_components,
|
|
297
317
|
winsor_p=winsor_p,
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
"models": C.model_fingerprint(loci, source)}), mats
|
|
318
|
+
# One manifest builder for both halves: a `vsig_<name>`/`rsig_<name>` pair that disagreed
|
|
319
|
+
# about the depths or the singleton fractions would be a joinability claim nobody can check.
|
|
320
|
+
meta=C.corpus_meta(regime, cohort, loci, n_samples=n_samples, size=size, seed=seed,
|
|
321
|
+
source=source, depth_spread=depth_spread,
|
|
322
|
+
mir_version=mir.__version__, species=species, K=F.K)), mats
|
|
304
323
|
|
|
305
324
|
|
|
306
325
|
def _demo() -> None:
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"K": 256,
|
|
3
|
-
"content_sha256": "02be6824a511fca836f59eeaf51fcdd1dfe98d6f8d36cf80cfeb8731f28c3d22",
|
|
4
|
-
"k": {
|
|
5
|
-
"IGH": 128,
|
|
6
|
-
"IGK": 128,
|
|
7
|
-
"IGL": 128,
|
|
8
|
-
"TRA": 128,
|
|
9
|
-
"TRB": 128,
|
|
10
|
-
"TRD": 128,
|
|
11
|
-
"TRG": 128
|
|
12
|
-
},
|
|
13
|
-
"loci": [
|
|
14
|
-
"TRA",
|
|
15
|
-
"TRB",
|
|
16
|
-
"TRG",
|
|
17
|
-
"TRD",
|
|
18
|
-
"IGH",
|
|
19
|
-
"IGK",
|
|
20
|
-
"IGL"
|
|
21
|
-
],
|
|
22
|
-
"mir_version": "3.20.2",
|
|
23
|
-
"mode": "features",
|
|
24
|
-
"models": {
|
|
25
|
-
"IGH": "olga:human_B_heavy@e21ec62f2139",
|
|
26
|
-
"IGK": "olga:human_B_kappa@f160ed8ce718",
|
|
27
|
-
"IGL": "olga:human_B_lambda@4756d868be49",
|
|
28
|
-
"TRA": "olga:human_T_alpha@2a4e83d16fdf",
|
|
29
|
-
"TRB": "olga:human_T_beta@2fccd0006de8",
|
|
30
|
-
"TRD": "olga:human_T_delta@def2922b705f",
|
|
31
|
-
"TRG": "olga:human_T_gamma@d25719222561"
|
|
32
|
-
},
|
|
33
|
-
"n_components_requested": 128,
|
|
34
|
-
"n_rows": {
|
|
35
|
-
"IGH": 10000,
|
|
36
|
-
"IGK": 10000,
|
|
37
|
-
"IGL": 10000,
|
|
38
|
-
"TRA": 10000,
|
|
39
|
-
"TRB": 10000,
|
|
40
|
-
"TRD": 10000,
|
|
41
|
-
"TRG": 10000
|
|
42
|
-
},
|
|
43
|
-
"n_samples": 10000,
|
|
44
|
-
"name": "memory",
|
|
45
|
-
"numpy": "2.5.2",
|
|
46
|
-
"platform": "Linux-5.4.0-215-generic-x86_64-with-glibc2.31",
|
|
47
|
-
"regime": "memory",
|
|
48
|
-
"seed": 20260927,
|
|
49
|
-
"sig": "rsig",
|
|
50
|
-
"size": 10000,
|
|
51
|
-
"size_per_locus": {
|
|
52
|
-
"IGH": 10000,
|
|
53
|
-
"IGK": 10000,
|
|
54
|
-
"IGL": 10000,
|
|
55
|
-
"TRA": 10000,
|
|
56
|
-
"TRB": 10000,
|
|
57
|
-
"TRD": 10000,
|
|
58
|
-
"TRG": 10000
|
|
59
|
-
},
|
|
60
|
-
"source": "olga",
|
|
61
|
-
"species": "human",
|
|
62
|
-
"trimmed_frac": {
|
|
63
|
-
"IGH": 0.010038860103626942,
|
|
64
|
-
"IGK": 0.010038860103626942,
|
|
65
|
-
"IGL": 0.010038860103626942,
|
|
66
|
-
"TRA": 0.010038860103626942,
|
|
67
|
-
"TRB": 0.010038860103626942,
|
|
68
|
-
"TRD": 0.010038860103626942,
|
|
69
|
-
"TRG": 0.010038860103626942
|
|
70
|
-
},
|
|
71
|
-
"variance_at_k": {
|
|
72
|
-
"IGH": 0.9910758445764348,
|
|
73
|
-
"IGK": 0.9993485868815669,
|
|
74
|
-
"IGL": 0.9989373382451864,
|
|
75
|
-
"TRA": 0.996276863840755,
|
|
76
|
-
"TRB": 0.9956540910199058,
|
|
77
|
-
"TRD": 0.9987458060164907,
|
|
78
|
-
"TRG": 0.9992160798179918
|
|
79
|
-
},
|
|
80
|
-
"vocab": {
|
|
81
|
-
"IGH": {},
|
|
82
|
-
"IGK": {},
|
|
83
|
-
"IGL": {},
|
|
84
|
-
"TRA": {},
|
|
85
|
-
"TRB": {},
|
|
86
|
-
"TRD": {},
|
|
87
|
-
"TRG": {}
|
|
88
|
-
},
|
|
89
|
-
"winsor_p": 0.01,
|
|
90
|
-
"winsor_ps": [
|
|
91
|
-
0.01,
|
|
92
|
-
0.05
|
|
93
|
-
]
|
|
94
|
-
}
|
|
Binary file
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"K": 256,
|
|
3
|
-
"content_sha256": "6cc52f9da4a790b4553a978327d9ed41e7036856861322648c0483c6b15519d1",
|
|
4
|
-
"k": {
|
|
5
|
-
"IGH": 128,
|
|
6
|
-
"IGK": 128,
|
|
7
|
-
"IGL": 128,
|
|
8
|
-
"TRA": 128,
|
|
9
|
-
"TRB": 128,
|
|
10
|
-
"TRD": 128,
|
|
11
|
-
"TRG": 128
|
|
12
|
-
},
|
|
13
|
-
"loci": [
|
|
14
|
-
"TRA",
|
|
15
|
-
"TRB",
|
|
16
|
-
"TRG",
|
|
17
|
-
"TRD",
|
|
18
|
-
"IGH",
|
|
19
|
-
"IGK",
|
|
20
|
-
"IGL"
|
|
21
|
-
],
|
|
22
|
-
"mir_version": "3.20.2",
|
|
23
|
-
"mode": "features",
|
|
24
|
-
"models": {
|
|
25
|
-
"IGH": "olga:human_B_heavy@e21ec62f2139",
|
|
26
|
-
"IGK": "olga:human_B_kappa@f160ed8ce718",
|
|
27
|
-
"IGL": "olga:human_B_lambda@4756d868be49",
|
|
28
|
-
"TRA": "olga:human_T_alpha@2a4e83d16fdf",
|
|
29
|
-
"TRB": "olga:human_T_beta@2fccd0006de8",
|
|
30
|
-
"TRD": "olga:human_T_delta@def2922b705f",
|
|
31
|
-
"TRG": "olga:human_T_gamma@d25719222561"
|
|
32
|
-
},
|
|
33
|
-
"n_components_requested": 128,
|
|
34
|
-
"n_rows": {
|
|
35
|
-
"IGH": 10000,
|
|
36
|
-
"IGK": 10000,
|
|
37
|
-
"IGL": 10000,
|
|
38
|
-
"TRA": 10000,
|
|
39
|
-
"TRB": 10000,
|
|
40
|
-
"TRD": 10000,
|
|
41
|
-
"TRG": 10000
|
|
42
|
-
},
|
|
43
|
-
"n_samples": 10000,
|
|
44
|
-
"name": "naive",
|
|
45
|
-
"numpy": "2.5.2",
|
|
46
|
-
"platform": "Linux-5.4.0-215-generic-x86_64-with-glibc2.31",
|
|
47
|
-
"regime": "naive",
|
|
48
|
-
"seed": 20260927,
|
|
49
|
-
"sig": "rsig",
|
|
50
|
-
"size": 10000,
|
|
51
|
-
"size_per_locus": {
|
|
52
|
-
"IGH": 10000,
|
|
53
|
-
"IGK": 10000,
|
|
54
|
-
"IGL": 10000,
|
|
55
|
-
"TRA": 10000,
|
|
56
|
-
"TRB": 10000,
|
|
57
|
-
"TRD": 10000,
|
|
58
|
-
"TRG": 10000
|
|
59
|
-
},
|
|
60
|
-
"source": "olga",
|
|
61
|
-
"species": "human",
|
|
62
|
-
"trimmed_frac": {
|
|
63
|
-
"IGH": 0.010012970168612191,
|
|
64
|
-
"IGK": 0.01001284046692607,
|
|
65
|
-
"IGL": 0.01001284046692607,
|
|
66
|
-
"TRA": 0.010012710765239949,
|
|
67
|
-
"TRB": 0.010012970168612191,
|
|
68
|
-
"TRD": 0.010012970168612191,
|
|
69
|
-
"TRG": 0.010012710765239949
|
|
70
|
-
},
|
|
71
|
-
"variance_at_k": {
|
|
72
|
-
"IGH": 0.9887366548085227,
|
|
73
|
-
"IGK": 0.9991668470828621,
|
|
74
|
-
"IGL": 0.9986028566065589,
|
|
75
|
-
"TRA": 0.9917447530288632,
|
|
76
|
-
"TRB": 0.9923344290339433,
|
|
77
|
-
"TRD": 0.9966133463827523,
|
|
78
|
-
"TRG": 0.9986828452151979
|
|
79
|
-
},
|
|
80
|
-
"vocab": {
|
|
81
|
-
"IGH": {},
|
|
82
|
-
"IGK": {},
|
|
83
|
-
"IGL": {},
|
|
84
|
-
"TRA": {},
|
|
85
|
-
"TRB": {},
|
|
86
|
-
"TRD": {},
|
|
87
|
-
"TRG": {}
|
|
88
|
-
},
|
|
89
|
-
"winsor_p": 0.01,
|
|
90
|
-
"winsor_ps": [
|
|
91
|
-
0.01,
|
|
92
|
-
0.05
|
|
93
|
-
]
|
|
94
|
-
}
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mirpy_lib-4.0.0 → mirpy_lib-4.2.0}/src/mir/resources/gene_library/build_region_annotations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|