tcri 0.9.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.
Files changed (74) hide show
  1. tcri-0.9.0/LICENSE +21 -0
  2. tcri-0.9.0/PKG-INFO +206 -0
  3. tcri-0.9.0/README.md +161 -0
  4. tcri-0.9.0/pyproject.toml +79 -0
  5. tcri-0.9.0/setup.cfg +4 -0
  6. tcri-0.9.0/tcri/__init__.py +33 -0
  7. tcri-0.9.0/tcri/_compute/__init__.py +15 -0
  8. tcri-0.9.0/tcri/_compute/_distance.py +60 -0
  9. tcri-0.9.0/tcri/_compute/_joint.py +189 -0
  10. tcri-0.9.0/tcri/_compute/_tables.py +410 -0
  11. tcri-0.9.0/tcri/_compute/_xp.py +62 -0
  12. tcri-0.9.0/tcri/_state/__init__.py +6 -0
  13. tcri-0.9.0/tcri/_state/keys.py +95 -0
  14. tcri-0.9.0/tcri/_state/schemas.py +108 -0
  15. tcri-0.9.0/tcri/_state/storage.py +277 -0
  16. tcri-0.9.0/tcri/_stats/__init__.py +23 -0
  17. tcri-0.9.0/tcri/_stats/_compare.py +90 -0
  18. tcri-0.9.0/tcri/_stats/_core.py +135 -0
  19. tcri-0.9.0/tcri/datasets/__init__.py +20 -0
  20. tcri-0.9.0/tcri/datasets/_simulate.py +648 -0
  21. tcri-0.9.0/tcri/diagnostics/__init__.py +19 -0
  22. tcri-0.9.0/tcri/diagnostics/_ppc.py +259 -0
  23. tcri-0.9.0/tcri/diagnostics/_training.py +78 -0
  24. tcri-0.9.0/tcri/get.py +142 -0
  25. tcri-0.9.0/tcri/model/__init__.py +4 -0
  26. tcri-0.9.0/tcri/model/_callbacks.py +148 -0
  27. tcri-0.9.0/tcri/model/_classifier.py +23 -0
  28. tcri-0.9.0/tcri/model/_model.py +738 -0
  29. tcri-0.9.0/tcri/model/_module.py +424 -0
  30. tcri-0.9.0/tcri/model/_priors.py +149 -0
  31. tcri-0.9.0/tcri/model/_training.py +272 -0
  32. tcri-0.9.0/tcri/plotting/__init__.py +24 -0
  33. tcri-0.9.0/tcri/plotting/_base.py +447 -0
  34. tcri-0.9.0/tcri/plotting/_colors.py +95 -0
  35. tcri-0.9.0/tcri/plotting/_delta.py +56 -0
  36. tcri-0.9.0/tcri/plotting/_entropy.py +30 -0
  37. tcri-0.9.0/tcri/plotting/_flux.py +28 -0
  38. tcri-0.9.0/tcri/plotting/_mutual_information.py +19 -0
  39. tcri-0.9.0/tcri/plotting/_sankey.py +46 -0
  40. tcri-0.9.0/tcri/preprocessing/__init__.py +4 -0
  41. tcri-0.9.0/tcri/preprocessing/_preprocessing.py +61 -0
  42. tcri-0.9.0/tcri/tools/__init__.py +27 -0
  43. tcri-0.9.0/tcri/tools/_delta.py +171 -0
  44. tcri-0.9.0/tcri/tools/_entropy.py +126 -0
  45. tcri-0.9.0/tcri/tools/_flux.py +118 -0
  46. tcri-0.9.0/tcri/tools/_joint.py +225 -0
  47. tcri-0.9.0/tcri/tools/_mutual_information.py +82 -0
  48. tcri-0.9.0/tcri/utils/__init__.py +10 -0
  49. tcri-0.9.0/tcri/utils/_utils.py +401 -0
  50. tcri-0.9.0/tcri.egg-info/PKG-INFO +206 -0
  51. tcri-0.9.0/tcri.egg-info/SOURCES.txt +72 -0
  52. tcri-0.9.0/tcri.egg-info/dependency_links.txt +1 -0
  53. tcri-0.9.0/tcri.egg-info/requires.txt +23 -0
  54. tcri-0.9.0/tcri.egg-info/top_level.txt +1 -0
  55. tcri-0.9.0/tests/test_contract_conformance.py +232 -0
  56. tcri-0.9.0/tests/test_guide_concentration.py +139 -0
  57. tcri-0.9.0/tests/test_helpers.py +203 -0
  58. tcri-0.9.0/tests/test_layout.py +204 -0
  59. tcri-0.9.0/tests/test_metrics_contract_conformance.py +388 -0
  60. tcri-0.9.0/tests/test_model_classifier.py +130 -0
  61. tcri-0.9.0/tests/test_model_contract_conformance.py +425 -0
  62. tcri-0.9.0/tests/test_model_determinism.py +79 -0
  63. tcri-0.9.0/tests/test_model_guardrails.py +208 -0
  64. tcri-0.9.0/tests/test_model_knobs.py +399 -0
  65. tcri-0.9.0/tests/test_model_setup.py +131 -0
  66. tcri-0.9.0/tests/test_model_smoke.py +65 -0
  67. tcri-0.9.0/tests/test_pyro_params.py +52 -0
  68. tcri-0.9.0/tests/test_real_data_yost.py +200 -0
  69. tcri-0.9.0/tests/test_recovery.py +314 -0
  70. tcri-0.9.0/tests/test_removal_ledger.py +130 -0
  71. tcri-0.9.0/tests/test_session_round_trip.py +137 -0
  72. tcri-0.9.0/tests/test_shared_defaults.py +81 -0
  73. tcri-0.9.0/tests/test_training_contract_conformance.py +162 -0
  74. tcri-0.9.0/tests/test_training_invariants.py +412 -0
tcri-0.9.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022-2026 Memorial Sloan Kettering Cancer Center
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tcri-0.9.0/PKG-INFO ADDED
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.4
2
+ Name: tcri
3
+ Version: 0.9.0
4
+ Summary: Information theoretic metrics for single cell RNA and TCR sequencing.
5
+ Author-email: Nicholas Ceglia <nickceglia@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/nceglia/tcri
8
+ Project-URL: Repository, https://github.com/nceglia/tcri
9
+ Project-URL: Issues, https://github.com/nceglia/tcri/issues
10
+ Project-URL: Documentation, https://tcri.readthedocs.io
11
+ Keywords: single-cell,TCR,scRNA-seq,immunology,information-theory,bayesian
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: numpy>=1.26.1
24
+ Requires-Dist: pandas>=2.2.2
25
+ Requires-Dist: scipy>=1.14.1
26
+ Requires-Dist: scikit-learn>=1.1
27
+ Requires-Dist: anndata>=0.11.3
28
+ Requires-Dist: scanpy>=1.9.5
29
+ Requires-Dist: torch>=2.4.1
30
+ Requires-Dist: pyro-ppl>=1.9.1
31
+ Requires-Dist: scvi-tools>=1.3.0
32
+ Requires-Dist: umap-learn>=0.5.6
33
+ Requires-Dist: matplotlib>=3.9.2
34
+ Requires-Dist: seaborn>=0.13.2
35
+ Requires-Dist: tqdm>=4.66.5
36
+ Provides-Extra: test
37
+ Requires-Dist: pytest>=7; extra == "test"
38
+ Requires-Dist: pytest-cov; extra == "test"
39
+ Provides-Extra: notebook
40
+ Requires-Dist: jupytext>=1.16; extra == "notebook"
41
+ Requires-Dist: nbclient>=0.10; extra == "notebook"
42
+ Requires-Dist: nbformat>=5.10; extra == "notebook"
43
+ Requires-Dist: ipykernel>=6; extra == "notebook"
44
+ Dynamic: license-file
45
+
46
+ <p align="center">
47
+ <a href="https://tcri.readthedocs.io">
48
+ <img src="https://raw.githubusercontent.com/nceglia/tcri/main/docs/images/tcri_logo_hero.png" alt="TCRi" width="420">
49
+ </a>
50
+ </p>
51
+
52
+ <p align="center">
53
+ <b>Information-theoretic analysis of paired single-cell RNA + TCR sequencing.</b>
54
+ </p>
55
+
56
+ <p align="center">
57
+ <a href="https://tcri.readthedocs.io">Documentation</a> ·
58
+ <a href="https://tcri.readthedocs.io/en/latest/tutorials/index.html">Tutorials</a> ·
59
+ <a href="https://www.biorxiv.org/content/10.1101/2022.10.01.510457v1">Paper</a> ·
60
+ <a href="https://github.com/nceglia/tcri/issues">Issues</a>
61
+ </p>
62
+
63
+ <p align="center">
64
+ <a href="https://github.com/nceglia/tcri/actions/workflows/tests.yml"><img src="https://github.com/nceglia/tcri/actions/workflows/tests.yml/badge.svg" alt="tests"></a>
65
+ <a href="https://tcri.readthedocs.io"><img src="https://readthedocs.org/projects/tcri/badge/?version=latest" alt="docs"></a>
66
+ <a href="https://github.com/nceglia/tcri/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license: MIT"></a>
67
+ <img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="python 3.10+">
68
+ <img src="https://img.shields.io/badge/built%20on-scverse-%231f9e16.svg" alt="built on scverse">
69
+ </p>
70
+
71
+ ---
72
+
73
+ TCRi quantifies how **T-cell clonotypes** relate to **transcriptional phenotypes** in
74
+ paired single-cell data. It fits a hierarchical Bayesian model (on top of
75
+ [scvi-tools](https://scvi-tools.org) / [Pyro](https://pyro.ai)) that ties each clone's
76
+ phenotype distribution to gene expression, then reads out **information-theoretic
77
+ summaries** — clonotypic and phenotypic entropy, clone↔phenotype mutual information, and
78
+ phenotypic flux across conditions — over the learned clone × phenotype joint distribution.
79
+
80
+ It is built on the [scverse](https://scverse.org) stack
81
+ ([AnnData](https://anndata.readthedocs.io), [scanpy](https://scanpy.readthedocs.io),
82
+ scvi-tools) and works on a standard `AnnData` carrying both a clonotype label and a
83
+ gene-expression matrix.
84
+
85
+ ## Key capabilities
86
+
87
+ - **Joint model of expression + repertoire** — a hierarchical VAE (`TCRIModel`) that
88
+ learns each clone's phenotype distribution while reconstructing counts with a ZINB
89
+ decoder.
90
+ - **Information-theoretic metrics** — clonotypic entropy `H(c|φ)`, phenotypic entropy
91
+ `H(φ|c)`, normalized mutual information `I(c;φ)`, and phenotypic flux between conditions,
92
+ all in bits over the posterior joint.
93
+ - **Uncertainty-aware** — every metric can be reported as a posterior mean ± HDI by
94
+ drawing from the fitted Dirichlet posterior (`n_samples > 0`).
95
+ - **Group comparisons** — paired/unpaired contrasts across cohorts (e.g. responders vs
96
+ non-responders) with direction probabilities.
97
+ - **Diagnostics** — posterior-predictive checks, phenotype calibration, and permutation
98
+ nulls for the metrics.
99
+ - **Contract-governed** — the public API, the generative model, the metric definitions,
100
+ and the training plan are each frozen by a machine-checked contract.
101
+
102
+ ## Installation
103
+
104
+ TCRi targets **Python ≥ 3.10**. Install from source:
105
+
106
+ ```bash
107
+ git clone https://github.com/nceglia/tcri.git
108
+ cd tcri
109
+ pip install .
110
+ ```
111
+
112
+ The heavy scientific stack (PyTorch, Pyro, scvi-tools, scanpy) is pulled in
113
+ automatically. A GPU is optional but speeds up model fitting. See the
114
+ [installation guide](https://tcri.readthedocs.io/en/latest/usage/installation.html) for
115
+ details.
116
+
117
+ ## Quickstart
118
+
119
+ ```python
120
+ import tcri
121
+
122
+ # 1. register clonotype / phenotype / covariate / batch columns on your AnnData
123
+ tcri.ml.TCRIModel.setup_anndata(
124
+ adata,
125
+ layer="counts",
126
+ clonotype_key="clone_id",
127
+ phenotype_key="phenotype",
128
+ covariate_key="timepoint",
129
+ batch_key="patient",
130
+ )
131
+
132
+ # 2. fit the model and write learned quantities back onto the AnnData
133
+ model = tcri.ml.TCRIModel(adata)
134
+ model.train(max_epochs=200, batch_size=512)
135
+ model.to_anndata(adata)
136
+
137
+ # 3. read out information-theoretic metrics (bits)
138
+ mi = tcri.tl.mutual_information(adata, covariate="pre", normalize_mode="average")
139
+ ce = tcri.tl.clonotypic_entropy(adata, covariate="pre")
140
+ flux = tcri.tl.phenotypic_flux(adata, cov_from="pre", cov_to="post")
141
+ ```
142
+
143
+ No paired data yet? `tcri.datasets.simulate_tcri()` returns a synthetic AnnData whose
144
+ true mutual information is known in closed form — the basis for the
145
+ [tutorials](https://tcri.readthedocs.io/en/latest/tutorials/index.html).
146
+
147
+ ## Framework
148
+
149
+ TCRi models the joint distribution of clonotype and phenotype hierarchically: a
150
+ clonotype-level prior `p_c` over phenotypes, a covariate-specific `p_ct` anchored to it,
151
+ and a per-cell phenotype that fuses a classifier on the latent embedding `z` with the
152
+ clone's local prior. The metrics are then pure functions of the learned clone × phenotype
153
+ joint. The full generative model and its plate diagram are documented under
154
+ [Concepts](https://tcri.readthedocs.io/en/latest/concepts/index.html).
155
+
156
+ <p align="center">
157
+ <img src="https://raw.githubusercontent.com/nceglia/tcri/main/docs/images/framework.png" alt="TCRi framework" width="760">
158
+ </p>
159
+
160
+ ## Governance
161
+
162
+ The public interface, the generative mathematics, the metric definitions and the training
163
+ plan are each frozen by a machine-checked contract. A published number only means something
164
+ if the definition behind it is stable, so a metric cannot be silently redefined and the model
165
+ the package claims to implement is the model it does implement.
166
+
167
+ A failing conformance test means *stop and decide* — never loosen a contract to make it pass.
168
+ See [Governance](https://tcri.readthedocs.io/en/latest/contracts/index.html) for what each
169
+ contract covers, and `governance/` in the repository for the contracts themselves.
170
+
171
+ ## Related tools
172
+
173
+ | Tool | Relationship |
174
+ |------|-------------|
175
+ | [scirpy](https://scirpy.readthedocs.io) | AIRR/TCR repertoire handling and analysis |
176
+ | [scvi-tools](https://scvi-tools.org) | probabilistic modelling backbone TCRi builds on |
177
+ | [scanpy](https://scanpy.readthedocs.io) / [AnnData](https://anndata.readthedocs.io) | single-cell data structures and workflows |
178
+
179
+ ## Citation
180
+
181
+ If you use TCRi in your research, please cite:
182
+
183
+ > **TCRi: An Information Theoretic Framework for Paired Single Cell Gene Expression and TCR Sequencing**
184
+ >
185
+ > Nicholas Ceglia, Sohrab Salehi, et al.
186
+ >
187
+ > _bioRxiv_ 2022. doi: [10.1101/2022.10.01.510457](https://doi.org/10.1101/2022.10.01.510457)
188
+
189
+ <details>
190
+ <summary>BibTeX</summary>
191
+
192
+ ```bibtex
193
+ @article{ceglia2022tcri,
194
+ title = {TCRi: An Information Theoretic Framework for Paired Single Cell Gene Expression and TCR Sequencing},
195
+ author = {Ceglia, Nicholas and Salehi, Sohrab and others},
196
+ journal = {bioRxiv},
197
+ year = {2022},
198
+ doi = {10.1101/2022.10.01.510457}
199
+ }
200
+ ```
201
+
202
+ </details>
203
+
204
+ ## License
205
+
206
+ TCRi is released under the [MIT License](LICENSE).
tcri-0.9.0/README.md ADDED
@@ -0,0 +1,161 @@
1
+ <p align="center">
2
+ <a href="https://tcri.readthedocs.io">
3
+ <img src="https://raw.githubusercontent.com/nceglia/tcri/main/docs/images/tcri_logo_hero.png" alt="TCRi" width="420">
4
+ </a>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <b>Information-theoretic analysis of paired single-cell RNA + TCR sequencing.</b>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://tcri.readthedocs.io">Documentation</a> ·
13
+ <a href="https://tcri.readthedocs.io/en/latest/tutorials/index.html">Tutorials</a> ·
14
+ <a href="https://www.biorxiv.org/content/10.1101/2022.10.01.510457v1">Paper</a> ·
15
+ <a href="https://github.com/nceglia/tcri/issues">Issues</a>
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="https://github.com/nceglia/tcri/actions/workflows/tests.yml"><img src="https://github.com/nceglia/tcri/actions/workflows/tests.yml/badge.svg" alt="tests"></a>
20
+ <a href="https://tcri.readthedocs.io"><img src="https://readthedocs.org/projects/tcri/badge/?version=latest" alt="docs"></a>
21
+ <a href="https://github.com/nceglia/tcri/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license: MIT"></a>
22
+ <img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="python 3.10+">
23
+ <img src="https://img.shields.io/badge/built%20on-scverse-%231f9e16.svg" alt="built on scverse">
24
+ </p>
25
+
26
+ ---
27
+
28
+ TCRi quantifies how **T-cell clonotypes** relate to **transcriptional phenotypes** in
29
+ paired single-cell data. It fits a hierarchical Bayesian model (on top of
30
+ [scvi-tools](https://scvi-tools.org) / [Pyro](https://pyro.ai)) that ties each clone's
31
+ phenotype distribution to gene expression, then reads out **information-theoretic
32
+ summaries** — clonotypic and phenotypic entropy, clone↔phenotype mutual information, and
33
+ phenotypic flux across conditions — over the learned clone × phenotype joint distribution.
34
+
35
+ It is built on the [scverse](https://scverse.org) stack
36
+ ([AnnData](https://anndata.readthedocs.io), [scanpy](https://scanpy.readthedocs.io),
37
+ scvi-tools) and works on a standard `AnnData` carrying both a clonotype label and a
38
+ gene-expression matrix.
39
+
40
+ ## Key capabilities
41
+
42
+ - **Joint model of expression + repertoire** — a hierarchical VAE (`TCRIModel`) that
43
+ learns each clone's phenotype distribution while reconstructing counts with a ZINB
44
+ decoder.
45
+ - **Information-theoretic metrics** — clonotypic entropy `H(c|φ)`, phenotypic entropy
46
+ `H(φ|c)`, normalized mutual information `I(c;φ)`, and phenotypic flux between conditions,
47
+ all in bits over the posterior joint.
48
+ - **Uncertainty-aware** — every metric can be reported as a posterior mean ± HDI by
49
+ drawing from the fitted Dirichlet posterior (`n_samples > 0`).
50
+ - **Group comparisons** — paired/unpaired contrasts across cohorts (e.g. responders vs
51
+ non-responders) with direction probabilities.
52
+ - **Diagnostics** — posterior-predictive checks, phenotype calibration, and permutation
53
+ nulls for the metrics.
54
+ - **Contract-governed** — the public API, the generative model, the metric definitions,
55
+ and the training plan are each frozen by a machine-checked contract.
56
+
57
+ ## Installation
58
+
59
+ TCRi targets **Python ≥ 3.10**. Install from source:
60
+
61
+ ```bash
62
+ git clone https://github.com/nceglia/tcri.git
63
+ cd tcri
64
+ pip install .
65
+ ```
66
+
67
+ The heavy scientific stack (PyTorch, Pyro, scvi-tools, scanpy) is pulled in
68
+ automatically. A GPU is optional but speeds up model fitting. See the
69
+ [installation guide](https://tcri.readthedocs.io/en/latest/usage/installation.html) for
70
+ details.
71
+
72
+ ## Quickstart
73
+
74
+ ```python
75
+ import tcri
76
+
77
+ # 1. register clonotype / phenotype / covariate / batch columns on your AnnData
78
+ tcri.ml.TCRIModel.setup_anndata(
79
+ adata,
80
+ layer="counts",
81
+ clonotype_key="clone_id",
82
+ phenotype_key="phenotype",
83
+ covariate_key="timepoint",
84
+ batch_key="patient",
85
+ )
86
+
87
+ # 2. fit the model and write learned quantities back onto the AnnData
88
+ model = tcri.ml.TCRIModel(adata)
89
+ model.train(max_epochs=200, batch_size=512)
90
+ model.to_anndata(adata)
91
+
92
+ # 3. read out information-theoretic metrics (bits)
93
+ mi = tcri.tl.mutual_information(adata, covariate="pre", normalize_mode="average")
94
+ ce = tcri.tl.clonotypic_entropy(adata, covariate="pre")
95
+ flux = tcri.tl.phenotypic_flux(adata, cov_from="pre", cov_to="post")
96
+ ```
97
+
98
+ No paired data yet? `tcri.datasets.simulate_tcri()` returns a synthetic AnnData whose
99
+ true mutual information is known in closed form — the basis for the
100
+ [tutorials](https://tcri.readthedocs.io/en/latest/tutorials/index.html).
101
+
102
+ ## Framework
103
+
104
+ TCRi models the joint distribution of clonotype and phenotype hierarchically: a
105
+ clonotype-level prior `p_c` over phenotypes, a covariate-specific `p_ct` anchored to it,
106
+ and a per-cell phenotype that fuses a classifier on the latent embedding `z` with the
107
+ clone's local prior. The metrics are then pure functions of the learned clone × phenotype
108
+ joint. The full generative model and its plate diagram are documented under
109
+ [Concepts](https://tcri.readthedocs.io/en/latest/concepts/index.html).
110
+
111
+ <p align="center">
112
+ <img src="https://raw.githubusercontent.com/nceglia/tcri/main/docs/images/framework.png" alt="TCRi framework" width="760">
113
+ </p>
114
+
115
+ ## Governance
116
+
117
+ The public interface, the generative mathematics, the metric definitions and the training
118
+ plan are each frozen by a machine-checked contract. A published number only means something
119
+ if the definition behind it is stable, so a metric cannot be silently redefined and the model
120
+ the package claims to implement is the model it does implement.
121
+
122
+ A failing conformance test means *stop and decide* — never loosen a contract to make it pass.
123
+ See [Governance](https://tcri.readthedocs.io/en/latest/contracts/index.html) for what each
124
+ contract covers, and `governance/` in the repository for the contracts themselves.
125
+
126
+ ## Related tools
127
+
128
+ | Tool | Relationship |
129
+ |------|-------------|
130
+ | [scirpy](https://scirpy.readthedocs.io) | AIRR/TCR repertoire handling and analysis |
131
+ | [scvi-tools](https://scvi-tools.org) | probabilistic modelling backbone TCRi builds on |
132
+ | [scanpy](https://scanpy.readthedocs.io) / [AnnData](https://anndata.readthedocs.io) | single-cell data structures and workflows |
133
+
134
+ ## Citation
135
+
136
+ If you use TCRi in your research, please cite:
137
+
138
+ > **TCRi: An Information Theoretic Framework for Paired Single Cell Gene Expression and TCR Sequencing**
139
+ >
140
+ > Nicholas Ceglia, Sohrab Salehi, et al.
141
+ >
142
+ > _bioRxiv_ 2022. doi: [10.1101/2022.10.01.510457](https://doi.org/10.1101/2022.10.01.510457)
143
+
144
+ <details>
145
+ <summary>BibTeX</summary>
146
+
147
+ ```bibtex
148
+ @article{ceglia2022tcri,
149
+ title = {TCRi: An Information Theoretic Framework for Paired Single Cell Gene Expression and TCR Sequencing},
150
+ author = {Ceglia, Nicholas and Salehi, Sohrab and others},
151
+ journal = {bioRxiv},
152
+ year = {2022},
153
+ doi = {10.1101/2022.10.01.510457}
154
+ }
155
+ ```
156
+
157
+ </details>
158
+
159
+ ## License
160
+
161
+ TCRi is released under the [MIT License](LICENSE).
@@ -0,0 +1,79 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tcri"
7
+ version = "0.9.0"
8
+ description = "Information theoretic metrics for single cell RNA and TCR sequencing."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Nicholas Ceglia", email = "nickceglia@gmail.com" },
14
+ ]
15
+ keywords = [
16
+ "single-cell",
17
+ "TCR",
18
+ "scRNA-seq",
19
+ "immunology",
20
+ "information-theory",
21
+ "bayesian",
22
+ ]
23
+ classifiers = [
24
+ "Development Status :: 4 - Beta",
25
+ "Intended Audience :: Science/Research",
26
+ "Operating System :: OS Independent",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
32
+ ]
33
+ dependencies = [
34
+ "numpy>=1.26.1",
35
+ "pandas>=2.2.2",
36
+ "scipy>=1.14.1",
37
+ "scikit-learn>=1.1",
38
+ "anndata>=0.11.3",
39
+ "scanpy>=1.9.5",
40
+ "torch>=2.4.1",
41
+ "pyro-ppl>=1.9.1",
42
+ "scvi-tools>=1.3.0",
43
+ "umap-learn>=0.5.6",
44
+ "matplotlib>=3.9.2",
45
+ "seaborn>=0.13.2",
46
+ "tqdm>=4.66.5",
47
+ ]
48
+
49
+ [project.optional-dependencies]
50
+ test = [
51
+ "pytest>=7",
52
+ "pytest-cov",
53
+ ]
54
+ # Building examples/example.ipynb with its outputs embedded. Not needed to use tcri.
55
+ notebook = [
56
+ "jupytext>=1.16",
57
+ "nbclient>=0.10",
58
+ "nbformat>=5.10",
59
+ "ipykernel>=6",
60
+ ]
61
+
62
+ [project.urls]
63
+ Homepage = "https://github.com/nceglia/tcri"
64
+ Repository = "https://github.com/nceglia/tcri"
65
+ Issues = "https://github.com/nceglia/tcri/issues"
66
+ Documentation = "https://tcri.readthedocs.io"
67
+
68
+ [tool.setuptools.packages.find]
69
+ include = ["tcri*"]
70
+
71
+ [tool.pytest.ini_options]
72
+ testpaths = ["tests"]
73
+ python_files = ["test_*.py"]
74
+ python_classes = ["Test*"]
75
+ python_functions = ["test_*"]
76
+ addopts = "-v --tb=short"
77
+ filterwarnings = [
78
+ "ignore::DeprecationWarning",
79
+ ]
tcri-0.9.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,33 @@
1
+ from importlib.metadata import PackageNotFoundError as _PackageNotFoundError, version as _version
2
+
3
+ try:
4
+ __version__ = _version("tcri")
5
+ except _PackageNotFoundError: # running from a source tree without an install
6
+ __version__ = "0.0.0+unknown"
7
+
8
+ from . import tools as tl # PR6: tl repointed metrics -> tools (engine-backed metrics)
9
+ from . import preprocessing as pp
10
+ from . import plotting as pl
11
+ from . import utils as ut
12
+ from . import model as ml
13
+ from . import diagnostics as diag # PR8
14
+ from . import datasets # synthetic cohorts, incl. one with a known-MI oracle
15
+ from . import get # accessors for cached tl results
16
+
17
+ # The unified engine, re-exported top-level for prominence.
18
+ from .tools import joint_distribution
19
+
20
+ import sys as _sys
21
+
22
+ _sys.modules.update({f'{__name__}.{m}': globals()[m] for m in ['tl', 'pp', 'pl', 'ut', 'ml', 'diag']})
23
+
24
+ #: The top-level surface. Without this, ``dir(tcri)`` also advertised ``sys`` and
25
+ #: ``PackageNotFoundError`` -- module-machinery names that leaked purely because they were
26
+ #: imported at module scope, and which a user could reasonably have mistaken for API. The
27
+ #: private aliases (``_sys``, ``_PackageNotFoundError``) keep them out of ``dir`` regardless;
28
+ #: this makes the intended surface explicit rather than incidental.
29
+ __all__ = [
30
+ "tl", "pp", "pl", "ut", "ml", "diag", "datasets", "get",
31
+ "tools", "preprocessing", "plotting", "utils", "model", "diagnostics",
32
+ "joint_distribution", "__version__",
33
+ ]
@@ -0,0 +1,15 @@
1
+ """Private numeric + device seam for the engine (grafiti ``_compute`` parity).
2
+
3
+ - :mod:`._xp` — device dispatch (torch-first; CPU / torch-CUDA), ``asnumpy`` boundary.
4
+ - :mod:`._joint` — ``_joint_draws``: the batched ``[S, n_clones, P]`` engine core.
5
+ - :mod:`._distance` — kl / l1 / jsd kernels over phenotype distributions (was ``tcri/_distance.py``).
6
+ - :mod:`._tables` — the metric-table plumbing every ``tools`` metric reduces through
7
+ (``metric_table``, ``build_result``, ``build_stats``, ``collapse_to_replicates``); was
8
+ ``tcri/tools/_common.py``, which put shared machinery inside one of its own consumers.
9
+
10
+ This is the LOWER layer: ``tools``/``plotting``/``diagnostics`` import down into it and it must
11
+ not import back up. ``_tables`` needs one symbol from ``tools._joint`` and takes it lazily inside
12
+ the function, never at module scope.
13
+
14
+ Nothing here is public API; GPU libs are imported lazily inside functions.
15
+ """
@@ -0,0 +1,60 @@
1
+ """Distance / divergence kernels for phenotype distributions.
2
+
3
+ Single home for the KL kernel (was `metrics.dkl` + `flux.dkl_func`, two copies)
4
+ plus L1 and a symmetric Jensen–Shannon option. All operate on 1-D probability
5
+ vectors, use log base 2 (bits), and share one eps floor. ``phenotype_distance``
6
+ is the string→callable dispatcher used by ``phenotypic_flux(distance_metric=)``.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import numpy as np
11
+
12
+ EPS = 1e-12
13
+
14
+
15
+ def _normalize(p):
16
+ p = np.clip(np.asarray(p, float), EPS, None)
17
+ return p / p.sum()
18
+
19
+
20
+ def kl_divergence(p, q, *, base: float = 2.0, eps: float = EPS) -> float:
21
+ """KL(p ‖ q) in bits (base 2). Asymmetric."""
22
+ p = np.clip(np.asarray(p, float), eps, None); p = p / p.sum()
23
+ q = np.clip(np.asarray(q, float), eps, None); q = q / q.sum()
24
+ return float(np.sum(p * (np.log(p / q) / np.log(base))))
25
+
26
+
27
+ def l1_distance(p, q) -> float:
28
+ """L1 (Manhattan) distance between two normalized distributions, in [0, 2]."""
29
+ return float(np.abs(_normalize(p) - _normalize(q)).sum())
30
+
31
+
32
+ def jensen_shannon(p, q, *, base: float = 2.0, eps: float = EPS) -> float:
33
+ """Jensen–Shannon divergence: symmetric, bounded [0, 1] bit — the recommended
34
+ symmetric shift measure."""
35
+ p = _normalize(p)
36
+ q = _normalize(q)
37
+ m = 0.5 * (p + q)
38
+ return float(0.5 * kl_divergence(p, m, base=base, eps=eps)
39
+ + 0.5 * kl_divergence(q, m, base=base, eps=eps))
40
+
41
+
42
+ _REGISTRY = {
43
+ "l1": l1_distance,
44
+ "kl": kl_divergence,
45
+ "dkl": kl_divergence,
46
+ "js": jensen_shannon,
47
+ "jsd": jensen_shannon,
48
+ }
49
+
50
+
51
+ def phenotype_distance(metric):
52
+ """Resolve ``distance_metric`` (a name or a callable ``f(p, q) -> float``)."""
53
+ if callable(metric):
54
+ return metric
55
+ key = str(metric).lower()
56
+ if key not in _REGISTRY:
57
+ raise ValueError(
58
+ f"distance_metric must be a callable or one of {sorted(_REGISTRY)}; got {metric!r}"
59
+ )
60
+ return _REGISTRY[key]