bsvae 0.3.2__tar.gz → 0.4.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 (63) hide show
  1. bsvae-0.4.0/PKG-INFO +353 -0
  2. bsvae-0.4.0/README.md +313 -0
  3. {bsvae-0.3.2 → bsvae-0.4.0}/pyproject.toml +15 -2
  4. bsvae-0.4.0/src/bsvae/benchmark/__init__.py +2 -0
  5. bsvae-0.4.0/src/bsvae/benchmark/acorde.R +113 -0
  6. bsvae-0.4.0/src/bsvae/benchmark/edges.py +123 -0
  7. bsvae-0.4.0/src/bsvae/benchmark/harness.py +508 -0
  8. bsvae-0.4.0/src/bsvae/benchmark/methods.py +337 -0
  9. bsvae-0.4.0/src/bsvae/benchmark/runners.py +328 -0
  10. bsvae-0.4.0/src/bsvae/benchmark/twn.R +92 -0
  11. bsvae-0.4.0/src/bsvae/benchmark/wgcna.R +128 -0
  12. bsvae-0.4.0/src/bsvae/cli/_common.py +365 -0
  13. bsvae-0.4.0/src/bsvae/cli/benchmark.py +153 -0
  14. bsvae-0.4.0/src/bsvae/cli/networks.py +917 -0
  15. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/cli/simulate.py +52 -17
  16. bsvae-0.4.0/src/bsvae/cli/sweep_k.py +404 -0
  17. bsvae-0.4.0/src/bsvae/cli/train.py +119 -0
  18. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/latent/latent_analysis.py +2 -1
  19. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/latent/latent_export.py +2 -1
  20. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/models/encoder.py +87 -85
  21. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/models/gmm_prior.py +152 -49
  22. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/models/gmvae.py +5 -0
  23. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/models/losses.py +832 -632
  24. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/networks/extract_networks.py +22 -34
  25. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/networks/module_extraction.py +60 -62
  26. bsvae-0.4.0/src/bsvae/networks/postprocess.py +666 -0
  27. bsvae-0.4.0/src/bsvae/networks/quality.py +159 -0
  28. bsvae-0.4.0/src/bsvae/networks/targets.py +665 -0
  29. bsvae-0.4.0/src/bsvae/simulation/isoform.py +423 -0
  30. bsvae-0.4.0/src/bsvae/simulation/metrics.py +456 -0
  31. bsvae-0.4.0/src/bsvae/simulation/scenario.py +863 -0
  32. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/utils/datasets.py +62 -9
  33. bsvae-0.4.0/src/bsvae/utils/hierarchy.py +342 -0
  34. bsvae-0.4.0/src/bsvae/utils/isoform.py +519 -0
  35. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/utils/modelIO.py +3 -0
  36. bsvae-0.4.0/src/bsvae/utils/preprocess.py +131 -0
  37. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/utils/training.py +185 -77
  38. bsvae-0.3.2/PKG-INFO +0 -224
  39. bsvae-0.3.2/README.md +0 -192
  40. bsvae-0.3.2/src/bsvae/cli/networks.py +0 -346
  41. bsvae-0.3.2/src/bsvae/cli/sweep_k.py +0 -511
  42. bsvae-0.3.2/src/bsvae/cli/train.py +0 -312
  43. bsvae-0.3.2/src/bsvae/hyperparam.ini +0 -9
  44. bsvae-0.3.2/src/bsvae/main.py +0 -364
  45. bsvae-0.3.2/src/bsvae/networks/cli.py +0 -689
  46. bsvae-0.3.2/src/bsvae/simulation/metrics.py +0 -154
  47. bsvae-0.3.2/src/bsvae/simulation/scenario.py +0 -611
  48. bsvae-0.3.2/src/bsvae/utils/evaluate.py +0 -84
  49. bsvae-0.3.2/src/bsvae/utils/hierarchy.py +0 -172
  50. bsvae-0.3.2/src/bsvae/utils/initialization.py +0 -30
  51. {bsvae-0.3.2 → bsvae-0.4.0}/LICENSE +0 -0
  52. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/__init__.py +0 -0
  53. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/cli/__init__.py +0 -0
  54. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/latent/__init__.py +0 -0
  55. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/models/__init__.py +0 -0
  56. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/models/decoder.py +0 -0
  57. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/models/vae.py +0 -0
  58. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/networks/__init__.py +0 -0
  59. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/networks/utils.py +0 -0
  60. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/simulation/__init__.py +0 -0
  61. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/simulation/generate.py +0 -0
  62. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/utils/__init__.py +0 -0
  63. {bsvae-0.3.2 → bsvae-0.4.0}/src/bsvae/utils/helpers.py +0 -0
bsvae-0.4.0/PKG-INFO ADDED
@@ -0,0 +1,353 @@
1
+ Metadata-Version: 2.4
2
+ Name: bsvae
3
+ Version: 0.4.0
4
+ Summary: Gaussian Mixture VAE for multi-modal biological module discovery in omics data.
5
+ License: GPL-3.0-only
6
+ License-File: LICENSE
7
+ Keywords: variational-autoencoder,gaussian-mixture-model,pytorch,bioinformatics,gene-expression,module-discovery,omics
8
+ Author: Kynon J Benjamin
9
+ Author-email: kj.benjamin90@gmail.com
10
+ Requires-Python: >=3.11,<3.14
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
21
+ Provides-Extra: anndata
22
+ Provides-Extra: yaml
23
+ Requires-Dist: anndata (>=0.10) ; extra == "anndata"
24
+ Requires-Dist: faiss-cpu (>=1.7)
25
+ Requires-Dist: h5py (>=3.0)
26
+ Requires-Dist: numpy (<2.3)
27
+ Requires-Dist: pandas (>=2.3.2,<3.0.0)
28
+ Requires-Dist: pyyaml (>=6.0) ; extra == "yaml"
29
+ Requires-Dist: scikit-learn (>=1.7.2,<2.0.0)
30
+ Requires-Dist: scipy (>=1.16.2,<2.0.0)
31
+ Requires-Dist: torch (>=2.8.0,<3.0.0)
32
+ Requires-Dist: tqdm (>=4.67.1,<5.0.0)
33
+ Project-URL: Bug Tracker, https://github.com/heart-gen/BSVAE/issues
34
+ Project-URL: Changelog, https://github.com/heart-gen/BSVAE/blob/main/CHANGELOG.md
35
+ Project-URL: documentation, https://bsvae.readthedocs.io/en/latest/
36
+ Project-URL: homepage, https://bsvae.readthedocs.io/en/latest/
37
+ Project-URL: repository, https://github.com/heart-gen/BSVAE
38
+ Description-Content-Type: text/markdown
39
+
40
+ # BSVAE: Gaussian Mixture VAE for Module Discovery
41
+
42
+ [![PyPI](https://img.shields.io/pypi/v/bsvae.svg)](https://pypi.org/project/bsvae/)
43
+ [![PyTorch](https://img.shields.io/badge/PyTorch-%3E%3D2.8-orange)](https://pytorch.org/)
44
+ [![Python](https://img.shields.io/badge/Python-%3E%3D3.11-blue)](https://python.org/)
45
+ [![License](https://img.shields.io/badge/license-GPLv3-blue.svg)](LICENSE)
46
+ [![Documentation](https://readthedocs.org/projects/bsvae/badge/?version=latest)](https://bsvae.readthedocs.io/)
47
+
48
+ BSVAE is a PyTorch package for feature-level module discovery in omics data. The main model, `GMMModuleVAE`, learns latent structure across features such as genes, transcripts, or proteins, then supports downstream network extraction, module assignment, latent analysis, and simulation-based benchmarking.
49
+
50
+ ## What The Package Provides
51
+
52
+ - `bsvae-train` for training a two-phase GMM-VAE
53
+ - `bsvae-sweep-k` for selecting the number of modules (`K`) with held-out validation and optional stability replicates
54
+ - `bsvae-networks` for post-training network extraction, module extraction (with gene coherence and an isoform-specific residual scan at transcript level), targeted-gene tests, latent export, and latent analysis
55
+ - `bsvae-simulate` for synthetic data generation, scenario-grid simulation, and module-recovery benchmarking
56
+ - `bsvae-benchmark` for head-to-head runs of BSVAE, WGCNA, acorde, usage networks, ICA/NMF and TWN on simulated scenario grids
57
+
58
+ ## Installation
59
+
60
+ Install from PyPI:
61
+
62
+ ```bash
63
+ pip install bsvae
64
+ ```
65
+
66
+ Install from source:
67
+
68
+ ```bash
69
+ git clone https://github.com/heart-gen/BSVAE.git
70
+ cd BSVAE
71
+ pip install -e .
72
+ ```
73
+
74
+ Optional dependencies:
75
+
76
+ - `anndata` is only needed for `.h5ad` input or `.h5ad` latent export helpers
77
+ - `pyyaml` is only needed to read YAML scenario configs (JSON configs need nothing)
78
+ - R with `WGCNA` (plus `glasso` for TWN) is only needed for the WGCNA, acorde and TWN methods of `bsvae-benchmark`; check with `bsvae-benchmark check-env`
79
+
80
+ ```bash
81
+ pip install "bsvae[anndata,yaml]"
82
+ ```
83
+
84
+ For development, use Poetry: `poetry install --with dev --extras "anndata yaml"`.
85
+
86
+ ## Input Data Contract
87
+
88
+ Training and most downstream commands expect an expression matrix in `features x samples` orientation:
89
+
90
+ - rows are features such as genes, transcripts, or proteins
91
+ - columns are sample IDs
92
+ - the first column is the feature index when using CSV/TSV
93
+
94
+ Supported loaders:
95
+
96
+ - `.csv` / `.csv.gz`
97
+ - `.tsv` / `.tsv.gz`
98
+ - `.h5` / `.hdf5`
99
+ - `.h5ad` with optional `anndata`
100
+
101
+ ## CLI Entry Points
102
+
103
+ - `bsvae-train`
104
+ - `bsvae-sweep-k`
105
+ - `bsvae-networks`
106
+ - `bsvae-simulate`
107
+ - `bsvae-benchmark` (BSVAE vs WGCNA, transcript-usage networks, ICA/NMF, acorde and TWN on simulated data; needs R with `WGCNA`, plus `glasso` for TWN)
108
+
109
+ At transcript level, `extract-modules` returns two independent outputs: coherent module assignments (the gene-coherence policy is the default whenever `--expr` and `--tx2gene` are given) and the isoform-specific residual scan over every multi-isoform transcript, so module membership and isoform-specific divergence are reported and validated separately. Further transcript-level commands in `bsvae-networks`: `consensus-modules` (majority vote over seeds with a per-feature stability score), `isoform-specific` (isoforms whose expression tracks a module other than their gene's), `target-genes` (calibrated membership tests for candidate genes or GWAS loci from a gene list, transcript list or BED file, with a case/control interaction test), `target-genes-combine` (pool those tests across cohorts or brain regions), `module-quality` (kME, enrichment, preservation in a second dataset) and `regress-covariates` (remove batch and other known covariates before training).
110
+
111
+ ### `bsvae-networks` subcommands
112
+
113
+ | Subcommand | Purpose |
114
+ | --- | --- |
115
+ | `extract-networks` | feature-feature networks (`mu_cosine`, `gamma_knn`) |
116
+ | `extract-modules` | post-processed modules, eigengenes, kME; at transcript level also gene coherence and the isoform-specific scan |
117
+ | `consensus-modules` | majority vote over seeds with a per-feature stability score |
118
+ | `isoform-specific` | isoforms whose expression tracks a module other than their gene's, optionally cross-fitted (`--folds`) |
119
+ | `isoform-residuals` | the gene-programme-removed residual matrix |
120
+ | `target-genes`, `target-genes-combine` | calibrated tests for candidate genes or GWAS loci, and pooling across cohorts |
121
+ | `module-quality` | kME, GMT enrichment, preservation in a second dataset |
122
+ | `regress-covariates` | remove batch and other known covariates before training |
123
+ | `export-latents`, `latent-analysis` | latent arrays, clustering and embeddings |
124
+
125
+ Flags worth knowing at transcript level: `--tx2gene`, `--covariates`, `--loo-eigengene {approx,exact,off}`, `--max-target-corr`, `--samples` / `--exclude-samples` (train fold models for cross-fitting), `--aggregate-to-gene`, and `--amp` on `bsvae-train` for mixed precision. See the [CLI reference](https://bsvae.readthedocs.io/en/latest/cli.html) for every flag and default.
126
+
127
+ ## Quickstart
128
+
129
+ Train a small model:
130
+
131
+ ```bash
132
+ bsvae-train pilot_run \
133
+ --dataset data/expression.csv \
134
+ --epochs 50 \
135
+ --n-modules 12 \
136
+ --latent-dim 16
137
+ ```
138
+
139
+ Recommended production flow: train with a generous number of GMM components, about three times the number of modules you expect (at least 20), and let `extract-modules` merge components whose eigengenes correlate (see [Choosing K](https://bsvae.readthedocs.io/en/latest/hyperparameters.html#choosing-k)). `bsvae-sweep-k` is an optional check of that component count by held-out ELBO and, with `--stability-reps`, by seed stability; it retrains the selected model so the steps below can start from it.
140
+
141
+ ```bash
142
+ bsvae-sweep-k sweep1 \
143
+ --dataset data/expression.csv \
144
+ --k-grid 8,12,16,24,32 \
145
+ --sweep-epochs 60 \
146
+ --stability-reps 5 \
147
+ --val-frac 0.1
148
+ ```
149
+
150
+ This writes sweep outputs under `results/sweep1/sweep_k/` and retrains the selected model under `results/sweep1/final_k<K>/`.
151
+
152
+ Extract module assignments from the final model (post-processing adds a grey class, a minimum module size and eigengene merging):
153
+
154
+ ```bash
155
+ bsvae-networks extract-modules \
156
+ --model-path results/sweep1/final_k16 \
157
+ --dataset data/expression.csv \
158
+ --output-dir results/sweep1/final_k16/modules \
159
+ --expr data/expression.csv \
160
+ --soft-eigengenes
161
+ ```
162
+
163
+ With transcript-level data, pass `--tx2gene` as well. `extract-modules` then produces two independent outputs: module assignments annotated by the gene-coherence policy (default; `--no-gene-coherence` to disable) and the isoform-specific residual scan (`--no-isoform-calls` to skip, `--loo-eigengene` to change the leave-one-gene-out eigengene correction). A transcript's module label never decides whether it is tested. For calibrated calls, pass technical covariates (`--covariates`) and, where modules were learned on the same samples, cross-fit over sample folds (`isoform-specific --folds`).
164
+
165
+ ```bash
166
+ bsvae-networks extract-modules \
167
+ --model-path results/iso1 \
168
+ --dataset data/transcripts.tsv.gz \
169
+ --output-dir results/iso1/modules \
170
+ --expr data/transcripts.tsv.gz \
171
+ --tx2gene data/tx2gene.tsv
172
+ ```
173
+
174
+ This adds `modules_raw.csv`, `gene_coherence.csv` and `isoform_specific.csv` next to `modules.csv`; see the [CLI reference](docs/cli.md) for the columns.
175
+
176
+ Export latents:
177
+
178
+ ```bash
179
+ bsvae-networks export-latents \
180
+ --model-path results/sweep1/final_k16 \
181
+ --dataset data/expression.csv \
182
+ --output results/sweep1/final_k16/latents
183
+ ```
184
+
185
+ This command writes `results/sweep1/final_k16/latents.npz` containing `mu`, `logvar`, `gamma`, and `feature_ids`.
186
+
187
+ Extract feature-feature networks:
188
+
189
+ ```bash
190
+ bsvae-networks extract-networks \
191
+ --model-path results/sweep1/final_k16 \
192
+ --dataset data/expression.csv \
193
+ --output-dir results/sweep1/final_k16/networks \
194
+ --methods mu_cosine gamma_knn \
195
+ --top-k 50
196
+ ```
197
+
198
+ Run latent-space analysis:
199
+
200
+ ```bash
201
+ bsvae-networks latent-analysis \
202
+ --model-path results/sweep1/final_k16 \
203
+ --dataset data/expression.csv \
204
+ --output-dir results/sweep1/final_k16/latent_analysis \
205
+ --kmeans-k 16 \
206
+ --umap
207
+ ```
208
+
209
+ ## Worked Example: Transcript-Level Workflow On Simulated Data
210
+
211
+ This end-to-end example uses the same simulator as the [benchmark study](https://github.com/heart-gen/bsvae-benchmarking), so every file below has a known truth to compare against. It runs in minutes on a CPU. The real-data version of the same chain (residualise, `bsvae-sweep-k`, `extract-modules`, `extract-networks`, `export-latents`) is in the [tutorial](https://bsvae.readthedocs.io/en/latest/tutorial.html).
212
+
213
+ ```bash
214
+ # 1. simulate one transcript-level scenario (500 genes, 6 modules, 150 samples)
215
+ bsvae-simulate init-config --output iso.json --isoform # edit n_genes, n_modules, grid as needed
216
+ bsvae-simulate generate-grid --config iso.json --outdir sim --reps 1 --base-seed 13
217
+ SCEN=$(ls -d sim/scenarios/*/rep_000 | head -1)
218
+
219
+ # 2. choose K, then retrain the selected model (transcript-to-gene map enables the hierarchical loss)
220
+ bsvae-sweep-k iso1 --dataset $SCEN/expr/features_x_samples.tsv.gz \
221
+ --k-grid 12,18,24 --sweep-epochs 60 --stability-reps 2 --val-frac 0.1 \
222
+ --tx2gene $SCEN/truth/tx2gene.tsv --hier-strength 0.2 --isoform-stratified
223
+
224
+ # 3. two outputs: gene-coherent modules and the isoform-specific residual scan
225
+ bsvae-networks extract-modules --model-path results/iso1/final_k<K> \
226
+ --dataset $SCEN/expr/features_x_samples.tsv.gz --expr $SCEN/expr/features_x_samples.tsv.gz \
227
+ --tx2gene $SCEN/truth/tx2gene.tsv --covariates $SCEN/covariates.tsv.gz \
228
+ --loo-eigengene approx --max-target-corr 0.4 --soft-eigengenes \
229
+ --output-dir results/iso1/modules
230
+ ```
231
+
232
+ `results/iso1/modules/` now holds `modules.csv` (gene-coherent assignments, grey is `-1`), `modules_raw.csv`, `gene_coherence.csv`, `isoform_specific.csv`, `eigengenes.csv`, `kME.csv` and `gamma.npz`. Score the modules against the simulator's truth:
233
+
234
+ ```python
235
+ import pandas as pd
236
+ from sklearn.metrics import adjusted_rand_score
237
+
238
+ pred = pd.read_csv("results/iso1/modules/modules.csv", index_col="feature_id")["module"]
239
+ truth = pd.read_csv(f"{scen}/truth/modules_hard.csv", index_col="feature_id")["module"]
240
+ print(adjusted_rand_score(truth.loc[pred.index], pred)) # grey (-1) counted as its own label
241
+
242
+ calls = pd.read_csv("results/iso1/modules/isoform_specific.csv")
243
+ sw = pd.read_csv(f"{scen}/truth/switch_table.tsv.gz", sep="\t", index_col=0)
244
+ print(calls["called"].sum(), "calls;", sw["is_switched"].sum(), "truly switched isoforms")
245
+ ```
246
+
247
+ For calibrated calls when modules were learned on the same samples, train one fold model per sample split and cross-fit:
248
+
249
+ ```bash
250
+ bsvae-networks isoform-specific --dataset $SCEN/expr/features_x_samples.tsv.gz \
251
+ --tx2gene $SCEN/truth/tx2gene.tsv --covariates $SCEN/covariates.tsv.gz \
252
+ --modules fold0/modules/modules_raw.csv fold1/modules/modules_raw.csv --folds folds.tsv \
253
+ --max-target-corr 0.4 --output results/iso1/isoform_specific_crossfit.tsv
254
+ ```
255
+
256
+ Then test candidate genes against the same modules, and pool several cohorts:
257
+
258
+ ```bash
259
+ bsvae-networks target-genes --dataset data/transcripts.tsv.gz --tx2gene data/tx2gene.tsv \
260
+ --modules results/iso1/modules/modules.csv --genes candidates.txt --output-dir results/iso1/targets
261
+
262
+ bsvae-networks target-genes-combine \
263
+ --inputs DLPFC=results/dlpfc/targets/target_isoforms.tsv HIPP=results/hipp/targets/target_isoforms.tsv \
264
+ --n-samples DLPFC=160 HIPP=150 --output results/targets_combined.tsv
265
+ ```
266
+
267
+ ## Training Outputs
268
+
269
+ `bsvae-train` writes to `results/<experiment>/` by default:
270
+
271
+ - `model.pt`
272
+ - `specs.json`
273
+ - `train_losses.csv`
274
+ - `model-<epoch>.pt` checkpoints when `--checkpoint-every` is greater than zero
275
+
276
+ `bsvae-sweep-k` writes:
277
+
278
+ - `results/<name>/sweep_k/sweep_results.csv`
279
+ - `results/<name>/sweep_k/sweep_summary.json`
280
+ - `results/<name>/sweep_k/k<K>/rep_<rep>/...` per-sweep run outputs
281
+ - `results/<name>/final_k<K>/...` for the final retrained model when `--train-final` is enabled
282
+
283
+ ## Simulation and Benchmarking
284
+
285
+ Generate one synthetic dataset:
286
+
287
+ ```bash
288
+ bsvae-simulate generate \
289
+ --output data/sim_expr.csv \
290
+ --save-ground-truth data/sim_truth.csv
291
+ ```
292
+
293
+ Generate a scenario grid:
294
+
295
+ ```bash
296
+ bsvae-simulate init-config --output sim.yaml
297
+
298
+ bsvae-simulate generate-grid \
299
+ --config sim.yaml \
300
+ --outdir results/sim_pub_v1 \
301
+ --reps 30 \
302
+ --base-seed 13
303
+ ```
304
+
305
+ Validate the generated grid:
306
+
307
+ ```bash
308
+ bsvae-simulate validate-grid --grid-dir results/sim_pub_v1
309
+ ```
310
+
311
+ Each scenario replicate writes method-ready files under `results/sim_pub_v1/scenarios/<scenario_id>/rep_<rep>/`, including:
312
+
313
+ - `expr/features_x_samples.tsv.gz`
314
+ - `expr/samples_x_features.tsv.gz`
315
+ - `truth/modules_hard.csv` (null features are `-1`)
316
+ - `truth/tx2gene.tsv` and `truth/modules_gene.csv` for the transcript-level family (`bsvae-simulate init-config --isoform`)
317
+ - `method_inputs.json`
318
+
319
+ Run BSVAE and WGCNA head-to-head on a grid and summarise grey-aware ARI/NMI, best-match Jaccard, and gene-level recovery:
320
+
321
+ ```bash
322
+ bsvae-benchmark check-env # torch, Rscript, WGCNA
323
+ bsvae-benchmark run --config iso.json --outdir results/bench --methods bsvae,wgcna --seeds 0,1,2
324
+ bsvae-benchmark summarize --results results/bench/results.tsv --by scenario_id,method
325
+ ```
326
+
327
+ `run` is resumable and can be split across a cluster with `--rep-ids` and `--scenario-ids`. Comparison methods are `bsvae_raw`, `bsvae_hier`, `bsvae_isoform`, `wgcna_raw`, `acorde`, `twn`, `usage_network`, `ica` and `nmf`. In the companion study (10 replicates, transcript-level scenarios, 150 samples), mean all-feature ARI was:
328
+
329
+ | Method | ARI (all features) | Unassigned |
330
+ | --- | --- | --- |
331
+ | `bsvae` (gene coherence) | 0.78 | 10% |
332
+ | `bsvae_raw` | 0.70 | 10% |
333
+ | `wgcna` (gene coherence) | 0.65 | 0.3% |
334
+ | `wgcna_raw` | 0.54 | 0.3% |
335
+ | `acorde` | 0.44 | 35% |
336
+
337
+ These are simulation results; see [Benchmarking](https://bsvae.readthedocs.io/en/latest/benchmarking.html) for the pipeline, output files, matched-coverage metrics and caveats, including that the residual-scan defaults were not validated in every simulated cell.
338
+
339
+ ## Documentation
340
+
341
+ Full documentation is at [bsvae.readthedocs.io](https://bsvae.readthedocs.io/):
342
+
343
+ - [Installation](https://bsvae.readthedocs.io/en/latest/installation.html) and [Quick Start](https://bsvae.readthedocs.io/en/latest/quickstart.html)
344
+ - [Tutorial](https://bsvae.readthedocs.io/en/latest/tutorial.html) (real-data workflow and the simulated example)
345
+ - [CLI reference](https://bsvae.readthedocs.io/en/latest/cli.html), [network workflows](https://bsvae.readthedocs.io/en/latest/networks.html) and [hyperparameters](https://bsvae.readthedocs.io/en/latest/hyperparameters.html)
346
+ - [Benchmarking](https://bsvae.readthedocs.io/en/latest/benchmarking.html) and [HPC](https://bsvae.readthedocs.io/en/latest/hpc.html)
347
+
348
+ The same pages are in `docs/` in this repository. Release notes are in [CHANGELOG.md](CHANGELOG.md).
349
+
350
+ ## License
351
+
352
+ This project is licensed under the [GNU General Public License v3.0](LICENSE).
353
+
bsvae-0.4.0/README.md ADDED
@@ -0,0 +1,313 @@
1
+ # BSVAE: Gaussian Mixture VAE for Module Discovery
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/bsvae.svg)](https://pypi.org/project/bsvae/)
4
+ [![PyTorch](https://img.shields.io/badge/PyTorch-%3E%3D2.8-orange)](https://pytorch.org/)
5
+ [![Python](https://img.shields.io/badge/Python-%3E%3D3.11-blue)](https://python.org/)
6
+ [![License](https://img.shields.io/badge/license-GPLv3-blue.svg)](LICENSE)
7
+ [![Documentation](https://readthedocs.org/projects/bsvae/badge/?version=latest)](https://bsvae.readthedocs.io/)
8
+
9
+ BSVAE is a PyTorch package for feature-level module discovery in omics data. The main model, `GMMModuleVAE`, learns latent structure across features such as genes, transcripts, or proteins, then supports downstream network extraction, module assignment, latent analysis, and simulation-based benchmarking.
10
+
11
+ ## What The Package Provides
12
+
13
+ - `bsvae-train` for training a two-phase GMM-VAE
14
+ - `bsvae-sweep-k` for selecting the number of modules (`K`) with held-out validation and optional stability replicates
15
+ - `bsvae-networks` for post-training network extraction, module extraction (with gene coherence and an isoform-specific residual scan at transcript level), targeted-gene tests, latent export, and latent analysis
16
+ - `bsvae-simulate` for synthetic data generation, scenario-grid simulation, and module-recovery benchmarking
17
+ - `bsvae-benchmark` for head-to-head runs of BSVAE, WGCNA, acorde, usage networks, ICA/NMF and TWN on simulated scenario grids
18
+
19
+ ## Installation
20
+
21
+ Install from PyPI:
22
+
23
+ ```bash
24
+ pip install bsvae
25
+ ```
26
+
27
+ Install from source:
28
+
29
+ ```bash
30
+ git clone https://github.com/heart-gen/BSVAE.git
31
+ cd BSVAE
32
+ pip install -e .
33
+ ```
34
+
35
+ Optional dependencies:
36
+
37
+ - `anndata` is only needed for `.h5ad` input or `.h5ad` latent export helpers
38
+ - `pyyaml` is only needed to read YAML scenario configs (JSON configs need nothing)
39
+ - R with `WGCNA` (plus `glasso` for TWN) is only needed for the WGCNA, acorde and TWN methods of `bsvae-benchmark`; check with `bsvae-benchmark check-env`
40
+
41
+ ```bash
42
+ pip install "bsvae[anndata,yaml]"
43
+ ```
44
+
45
+ For development, use Poetry: `poetry install --with dev --extras "anndata yaml"`.
46
+
47
+ ## Input Data Contract
48
+
49
+ Training and most downstream commands expect an expression matrix in `features x samples` orientation:
50
+
51
+ - rows are features such as genes, transcripts, or proteins
52
+ - columns are sample IDs
53
+ - the first column is the feature index when using CSV/TSV
54
+
55
+ Supported loaders:
56
+
57
+ - `.csv` / `.csv.gz`
58
+ - `.tsv` / `.tsv.gz`
59
+ - `.h5` / `.hdf5`
60
+ - `.h5ad` with optional `anndata`
61
+
62
+ ## CLI Entry Points
63
+
64
+ - `bsvae-train`
65
+ - `bsvae-sweep-k`
66
+ - `bsvae-networks`
67
+ - `bsvae-simulate`
68
+ - `bsvae-benchmark` (BSVAE vs WGCNA, transcript-usage networks, ICA/NMF, acorde and TWN on simulated data; needs R with `WGCNA`, plus `glasso` for TWN)
69
+
70
+ At transcript level, `extract-modules` returns two independent outputs: coherent module assignments (the gene-coherence policy is the default whenever `--expr` and `--tx2gene` are given) and the isoform-specific residual scan over every multi-isoform transcript, so module membership and isoform-specific divergence are reported and validated separately. Further transcript-level commands in `bsvae-networks`: `consensus-modules` (majority vote over seeds with a per-feature stability score), `isoform-specific` (isoforms whose expression tracks a module other than their gene's), `target-genes` (calibrated membership tests for candidate genes or GWAS loci from a gene list, transcript list or BED file, with a case/control interaction test), `target-genes-combine` (pool those tests across cohorts or brain regions), `module-quality` (kME, enrichment, preservation in a second dataset) and `regress-covariates` (remove batch and other known covariates before training).
71
+
72
+ ### `bsvae-networks` subcommands
73
+
74
+ | Subcommand | Purpose |
75
+ | --- | --- |
76
+ | `extract-networks` | feature-feature networks (`mu_cosine`, `gamma_knn`) |
77
+ | `extract-modules` | post-processed modules, eigengenes, kME; at transcript level also gene coherence and the isoform-specific scan |
78
+ | `consensus-modules` | majority vote over seeds with a per-feature stability score |
79
+ | `isoform-specific` | isoforms whose expression tracks a module other than their gene's, optionally cross-fitted (`--folds`) |
80
+ | `isoform-residuals` | the gene-programme-removed residual matrix |
81
+ | `target-genes`, `target-genes-combine` | calibrated tests for candidate genes or GWAS loci, and pooling across cohorts |
82
+ | `module-quality` | kME, GMT enrichment, preservation in a second dataset |
83
+ | `regress-covariates` | remove batch and other known covariates before training |
84
+ | `export-latents`, `latent-analysis` | latent arrays, clustering and embeddings |
85
+
86
+ Flags worth knowing at transcript level: `--tx2gene`, `--covariates`, `--loo-eigengene {approx,exact,off}`, `--max-target-corr`, `--samples` / `--exclude-samples` (train fold models for cross-fitting), `--aggregate-to-gene`, and `--amp` on `bsvae-train` for mixed precision. See the [CLI reference](https://bsvae.readthedocs.io/en/latest/cli.html) for every flag and default.
87
+
88
+ ## Quickstart
89
+
90
+ Train a small model:
91
+
92
+ ```bash
93
+ bsvae-train pilot_run \
94
+ --dataset data/expression.csv \
95
+ --epochs 50 \
96
+ --n-modules 12 \
97
+ --latent-dim 16
98
+ ```
99
+
100
+ Recommended production flow: train with a generous number of GMM components, about three times the number of modules you expect (at least 20), and let `extract-modules` merge components whose eigengenes correlate (see [Choosing K](https://bsvae.readthedocs.io/en/latest/hyperparameters.html#choosing-k)). `bsvae-sweep-k` is an optional check of that component count by held-out ELBO and, with `--stability-reps`, by seed stability; it retrains the selected model so the steps below can start from it.
101
+
102
+ ```bash
103
+ bsvae-sweep-k sweep1 \
104
+ --dataset data/expression.csv \
105
+ --k-grid 8,12,16,24,32 \
106
+ --sweep-epochs 60 \
107
+ --stability-reps 5 \
108
+ --val-frac 0.1
109
+ ```
110
+
111
+ This writes sweep outputs under `results/sweep1/sweep_k/` and retrains the selected model under `results/sweep1/final_k<K>/`.
112
+
113
+ Extract module assignments from the final model (post-processing adds a grey class, a minimum module size and eigengene merging):
114
+
115
+ ```bash
116
+ bsvae-networks extract-modules \
117
+ --model-path results/sweep1/final_k16 \
118
+ --dataset data/expression.csv \
119
+ --output-dir results/sweep1/final_k16/modules \
120
+ --expr data/expression.csv \
121
+ --soft-eigengenes
122
+ ```
123
+
124
+ With transcript-level data, pass `--tx2gene` as well. `extract-modules` then produces two independent outputs: module assignments annotated by the gene-coherence policy (default; `--no-gene-coherence` to disable) and the isoform-specific residual scan (`--no-isoform-calls` to skip, `--loo-eigengene` to change the leave-one-gene-out eigengene correction). A transcript's module label never decides whether it is tested. For calibrated calls, pass technical covariates (`--covariates`) and, where modules were learned on the same samples, cross-fit over sample folds (`isoform-specific --folds`).
125
+
126
+ ```bash
127
+ bsvae-networks extract-modules \
128
+ --model-path results/iso1 \
129
+ --dataset data/transcripts.tsv.gz \
130
+ --output-dir results/iso1/modules \
131
+ --expr data/transcripts.tsv.gz \
132
+ --tx2gene data/tx2gene.tsv
133
+ ```
134
+
135
+ This adds `modules_raw.csv`, `gene_coherence.csv` and `isoform_specific.csv` next to `modules.csv`; see the [CLI reference](docs/cli.md) for the columns.
136
+
137
+ Export latents:
138
+
139
+ ```bash
140
+ bsvae-networks export-latents \
141
+ --model-path results/sweep1/final_k16 \
142
+ --dataset data/expression.csv \
143
+ --output results/sweep1/final_k16/latents
144
+ ```
145
+
146
+ This command writes `results/sweep1/final_k16/latents.npz` containing `mu`, `logvar`, `gamma`, and `feature_ids`.
147
+
148
+ Extract feature-feature networks:
149
+
150
+ ```bash
151
+ bsvae-networks extract-networks \
152
+ --model-path results/sweep1/final_k16 \
153
+ --dataset data/expression.csv \
154
+ --output-dir results/sweep1/final_k16/networks \
155
+ --methods mu_cosine gamma_knn \
156
+ --top-k 50
157
+ ```
158
+
159
+ Run latent-space analysis:
160
+
161
+ ```bash
162
+ bsvae-networks latent-analysis \
163
+ --model-path results/sweep1/final_k16 \
164
+ --dataset data/expression.csv \
165
+ --output-dir results/sweep1/final_k16/latent_analysis \
166
+ --kmeans-k 16 \
167
+ --umap
168
+ ```
169
+
170
+ ## Worked Example: Transcript-Level Workflow On Simulated Data
171
+
172
+ This end-to-end example uses the same simulator as the [benchmark study](https://github.com/heart-gen/bsvae-benchmarking), so every file below has a known truth to compare against. It runs in minutes on a CPU. The real-data version of the same chain (residualise, `bsvae-sweep-k`, `extract-modules`, `extract-networks`, `export-latents`) is in the [tutorial](https://bsvae.readthedocs.io/en/latest/tutorial.html).
173
+
174
+ ```bash
175
+ # 1. simulate one transcript-level scenario (500 genes, 6 modules, 150 samples)
176
+ bsvae-simulate init-config --output iso.json --isoform # edit n_genes, n_modules, grid as needed
177
+ bsvae-simulate generate-grid --config iso.json --outdir sim --reps 1 --base-seed 13
178
+ SCEN=$(ls -d sim/scenarios/*/rep_000 | head -1)
179
+
180
+ # 2. choose K, then retrain the selected model (transcript-to-gene map enables the hierarchical loss)
181
+ bsvae-sweep-k iso1 --dataset $SCEN/expr/features_x_samples.tsv.gz \
182
+ --k-grid 12,18,24 --sweep-epochs 60 --stability-reps 2 --val-frac 0.1 \
183
+ --tx2gene $SCEN/truth/tx2gene.tsv --hier-strength 0.2 --isoform-stratified
184
+
185
+ # 3. two outputs: gene-coherent modules and the isoform-specific residual scan
186
+ bsvae-networks extract-modules --model-path results/iso1/final_k<K> \
187
+ --dataset $SCEN/expr/features_x_samples.tsv.gz --expr $SCEN/expr/features_x_samples.tsv.gz \
188
+ --tx2gene $SCEN/truth/tx2gene.tsv --covariates $SCEN/covariates.tsv.gz \
189
+ --loo-eigengene approx --max-target-corr 0.4 --soft-eigengenes \
190
+ --output-dir results/iso1/modules
191
+ ```
192
+
193
+ `results/iso1/modules/` now holds `modules.csv` (gene-coherent assignments, grey is `-1`), `modules_raw.csv`, `gene_coherence.csv`, `isoform_specific.csv`, `eigengenes.csv`, `kME.csv` and `gamma.npz`. Score the modules against the simulator's truth:
194
+
195
+ ```python
196
+ import pandas as pd
197
+ from sklearn.metrics import adjusted_rand_score
198
+
199
+ pred = pd.read_csv("results/iso1/modules/modules.csv", index_col="feature_id")["module"]
200
+ truth = pd.read_csv(f"{scen}/truth/modules_hard.csv", index_col="feature_id")["module"]
201
+ print(adjusted_rand_score(truth.loc[pred.index], pred)) # grey (-1) counted as its own label
202
+
203
+ calls = pd.read_csv("results/iso1/modules/isoform_specific.csv")
204
+ sw = pd.read_csv(f"{scen}/truth/switch_table.tsv.gz", sep="\t", index_col=0)
205
+ print(calls["called"].sum(), "calls;", sw["is_switched"].sum(), "truly switched isoforms")
206
+ ```
207
+
208
+ For calibrated calls when modules were learned on the same samples, train one fold model per sample split and cross-fit:
209
+
210
+ ```bash
211
+ bsvae-networks isoform-specific --dataset $SCEN/expr/features_x_samples.tsv.gz \
212
+ --tx2gene $SCEN/truth/tx2gene.tsv --covariates $SCEN/covariates.tsv.gz \
213
+ --modules fold0/modules/modules_raw.csv fold1/modules/modules_raw.csv --folds folds.tsv \
214
+ --max-target-corr 0.4 --output results/iso1/isoform_specific_crossfit.tsv
215
+ ```
216
+
217
+ Then test candidate genes against the same modules, and pool several cohorts:
218
+
219
+ ```bash
220
+ bsvae-networks target-genes --dataset data/transcripts.tsv.gz --tx2gene data/tx2gene.tsv \
221
+ --modules results/iso1/modules/modules.csv --genes candidates.txt --output-dir results/iso1/targets
222
+
223
+ bsvae-networks target-genes-combine \
224
+ --inputs DLPFC=results/dlpfc/targets/target_isoforms.tsv HIPP=results/hipp/targets/target_isoforms.tsv \
225
+ --n-samples DLPFC=160 HIPP=150 --output results/targets_combined.tsv
226
+ ```
227
+
228
+ ## Training Outputs
229
+
230
+ `bsvae-train` writes to `results/<experiment>/` by default:
231
+
232
+ - `model.pt`
233
+ - `specs.json`
234
+ - `train_losses.csv`
235
+ - `model-<epoch>.pt` checkpoints when `--checkpoint-every` is greater than zero
236
+
237
+ `bsvae-sweep-k` writes:
238
+
239
+ - `results/<name>/sweep_k/sweep_results.csv`
240
+ - `results/<name>/sweep_k/sweep_summary.json`
241
+ - `results/<name>/sweep_k/k<K>/rep_<rep>/...` per-sweep run outputs
242
+ - `results/<name>/final_k<K>/...` for the final retrained model when `--train-final` is enabled
243
+
244
+ ## Simulation and Benchmarking
245
+
246
+ Generate one synthetic dataset:
247
+
248
+ ```bash
249
+ bsvae-simulate generate \
250
+ --output data/sim_expr.csv \
251
+ --save-ground-truth data/sim_truth.csv
252
+ ```
253
+
254
+ Generate a scenario grid:
255
+
256
+ ```bash
257
+ bsvae-simulate init-config --output sim.yaml
258
+
259
+ bsvae-simulate generate-grid \
260
+ --config sim.yaml \
261
+ --outdir results/sim_pub_v1 \
262
+ --reps 30 \
263
+ --base-seed 13
264
+ ```
265
+
266
+ Validate the generated grid:
267
+
268
+ ```bash
269
+ bsvae-simulate validate-grid --grid-dir results/sim_pub_v1
270
+ ```
271
+
272
+ Each scenario replicate writes method-ready files under `results/sim_pub_v1/scenarios/<scenario_id>/rep_<rep>/`, including:
273
+
274
+ - `expr/features_x_samples.tsv.gz`
275
+ - `expr/samples_x_features.tsv.gz`
276
+ - `truth/modules_hard.csv` (null features are `-1`)
277
+ - `truth/tx2gene.tsv` and `truth/modules_gene.csv` for the transcript-level family (`bsvae-simulate init-config --isoform`)
278
+ - `method_inputs.json`
279
+
280
+ Run BSVAE and WGCNA head-to-head on a grid and summarise grey-aware ARI/NMI, best-match Jaccard, and gene-level recovery:
281
+
282
+ ```bash
283
+ bsvae-benchmark check-env # torch, Rscript, WGCNA
284
+ bsvae-benchmark run --config iso.json --outdir results/bench --methods bsvae,wgcna --seeds 0,1,2
285
+ bsvae-benchmark summarize --results results/bench/results.tsv --by scenario_id,method
286
+ ```
287
+
288
+ `run` is resumable and can be split across a cluster with `--rep-ids` and `--scenario-ids`. Comparison methods are `bsvae_raw`, `bsvae_hier`, `bsvae_isoform`, `wgcna_raw`, `acorde`, `twn`, `usage_network`, `ica` and `nmf`. In the companion study (10 replicates, transcript-level scenarios, 150 samples), mean all-feature ARI was:
289
+
290
+ | Method | ARI (all features) | Unassigned |
291
+ | --- | --- | --- |
292
+ | `bsvae` (gene coherence) | 0.78 | 10% |
293
+ | `bsvae_raw` | 0.70 | 10% |
294
+ | `wgcna` (gene coherence) | 0.65 | 0.3% |
295
+ | `wgcna_raw` | 0.54 | 0.3% |
296
+ | `acorde` | 0.44 | 35% |
297
+
298
+ These are simulation results; see [Benchmarking](https://bsvae.readthedocs.io/en/latest/benchmarking.html) for the pipeline, output files, matched-coverage metrics and caveats, including that the residual-scan defaults were not validated in every simulated cell.
299
+
300
+ ## Documentation
301
+
302
+ Full documentation is at [bsvae.readthedocs.io](https://bsvae.readthedocs.io/):
303
+
304
+ - [Installation](https://bsvae.readthedocs.io/en/latest/installation.html) and [Quick Start](https://bsvae.readthedocs.io/en/latest/quickstart.html)
305
+ - [Tutorial](https://bsvae.readthedocs.io/en/latest/tutorial.html) (real-data workflow and the simulated example)
306
+ - [CLI reference](https://bsvae.readthedocs.io/en/latest/cli.html), [network workflows](https://bsvae.readthedocs.io/en/latest/networks.html) and [hyperparameters](https://bsvae.readthedocs.io/en/latest/hyperparameters.html)
307
+ - [Benchmarking](https://bsvae.readthedocs.io/en/latest/benchmarking.html) and [HPC](https://bsvae.readthedocs.io/en/latest/hpc.html)
308
+
309
+ The same pages are in `docs/` in this repository. Release notes are in [CHANGELOG.md](CHANGELOG.md).
310
+
311
+ ## License
312
+
313
+ This project is licensed under the [GNU General Public License v3.0](LICENSE).