bsvae 0.3.0__tar.gz → 0.3.2__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.
- bsvae-0.3.2/PKG-INFO +224 -0
- bsvae-0.3.2/README.md +192 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/pyproject.toml +2 -1
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/cli/networks.py +59 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/cli/simulate.py +98 -3
- bsvae-0.3.2/src/bsvae/cli/sweep_k.py +511 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/cli/train.py +91 -5
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/models/gmm_prior.py +17 -4
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/models/gmvae.py +20 -4
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/models/losses.py +234 -34
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/networks/extract_networks.py +13 -7
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/networks/module_extraction.py +4 -8
- bsvae-0.3.2/src/bsvae/simulation/__init__.py +24 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/simulation/generate.py +2 -2
- bsvae-0.3.2/src/bsvae/simulation/scenario.py +611 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/utils/modelIO.py +5 -4
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/utils/training.py +129 -12
- bsvae-0.3.0/PKG-INFO +0 -162
- bsvae-0.3.0/README.md +0 -130
- bsvae-0.3.0/src/bsvae/simulation/__init__.py +0 -5
- {bsvae-0.3.0 → bsvae-0.3.2}/LICENSE +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/__init__.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/cli/__init__.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/hyperparam.ini +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/latent/__init__.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/latent/latent_analysis.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/latent/latent_export.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/main.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/models/__init__.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/models/decoder.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/models/encoder.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/models/vae.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/networks/__init__.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/networks/cli.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/networks/utils.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/simulation/metrics.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/utils/__init__.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/utils/datasets.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/utils/evaluate.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/utils/helpers.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/utils/hierarchy.py +0 -0
- {bsvae-0.3.0 → bsvae-0.3.2}/src/bsvae/utils/initialization.py +0 -0
bsvae-0.3.2/PKG-INFO
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bsvae
|
|
3
|
+
Version: 0.3.2
|
|
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: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
19
|
+
Requires-Dist: faiss-cpu (>=1.7)
|
|
20
|
+
Requires-Dist: h5py (>=3.0)
|
|
21
|
+
Requires-Dist: numpy (<2.3)
|
|
22
|
+
Requires-Dist: pandas (>=2.3.2,<3.0.0)
|
|
23
|
+
Requires-Dist: scikit-learn (>=1.7.2,<2.0.0)
|
|
24
|
+
Requires-Dist: scipy (>=1.16.2,<2.0.0)
|
|
25
|
+
Requires-Dist: torch (>=2.8.0,<3.0.0)
|
|
26
|
+
Requires-Dist: tqdm (>=4.67.1,<5.0.0)
|
|
27
|
+
Project-URL: Bug Tracker, https://github.com/heart-gen/BSVAE/issues
|
|
28
|
+
Project-URL: homepage, https://bsvae.readthedocs.io/en/latest/
|
|
29
|
+
Project-URL: repository, https://github.com/heart-gen/BSVAE
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# BSVAE: Gaussian Mixture VAE for Module Discovery
|
|
33
|
+
|
|
34
|
+
[](https://pytorch.org/)
|
|
35
|
+
[](https://python.org/)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
[](https://bsvae.readthedocs.io/)
|
|
38
|
+
|
|
39
|
+
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.
|
|
40
|
+
|
|
41
|
+
## What The Package Provides
|
|
42
|
+
|
|
43
|
+
- `bsvae-train` for training a two-phase GMM-VAE
|
|
44
|
+
- `bsvae-sweep-k` for selecting the number of modules (`K`) with held-out validation and optional stability replicates
|
|
45
|
+
- `bsvae-networks` for post-training network extraction, module extraction, latent export, and latent analysis
|
|
46
|
+
- `bsvae-simulate` for synthetic data generation, scenario-grid simulation, and module-recovery benchmarking
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
Install from PyPI:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install bsvae
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Install from source:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/heart-gen/BSVAE.git
|
|
60
|
+
cd BSVAE
|
|
61
|
+
pip install -e .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Optional dependency:
|
|
65
|
+
|
|
66
|
+
- `anndata` is only needed for `.h5ad` input or `.h5ad` latent export helpers
|
|
67
|
+
|
|
68
|
+
## Input Data Contract
|
|
69
|
+
|
|
70
|
+
Training and most downstream commands expect an expression matrix in `features x samples` orientation:
|
|
71
|
+
|
|
72
|
+
- rows are features such as genes, transcripts, or proteins
|
|
73
|
+
- columns are sample IDs
|
|
74
|
+
- the first column is the feature index when using CSV/TSV
|
|
75
|
+
|
|
76
|
+
Supported loaders:
|
|
77
|
+
|
|
78
|
+
- `.csv` / `.csv.gz`
|
|
79
|
+
- `.tsv` / `.tsv.gz`
|
|
80
|
+
- `.h5` / `.hdf5`
|
|
81
|
+
- `.h5ad` with optional `anndata`
|
|
82
|
+
|
|
83
|
+
## CLI Entry Points
|
|
84
|
+
|
|
85
|
+
- `bsvae-train`
|
|
86
|
+
- `bsvae-sweep-k`
|
|
87
|
+
- `bsvae-networks`
|
|
88
|
+
- `bsvae-simulate`
|
|
89
|
+
|
|
90
|
+
## Quickstart
|
|
91
|
+
|
|
92
|
+
Train a small model:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
bsvae-train pilot_run \
|
|
96
|
+
--dataset data/expression.csv \
|
|
97
|
+
--epochs 50 \
|
|
98
|
+
--n-modules 12 \
|
|
99
|
+
--latent-dim 16
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Recommended production flow: select `K` first, then use the retrained final model.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
bsvae-sweep-k sweep1 \
|
|
106
|
+
--dataset data/expression.csv \
|
|
107
|
+
--k-grid 8,12,16,24,32 \
|
|
108
|
+
--sweep-epochs 60 \
|
|
109
|
+
--stability-reps 5 \
|
|
110
|
+
--val-frac 0.1
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
This writes sweep outputs under `results/sweep1/sweep_k/` and retrains the selected model under `results/sweep1/final_k<K>/`.
|
|
114
|
+
|
|
115
|
+
Extract module assignments from the final model:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
bsvae-networks extract-modules \
|
|
119
|
+
--model-path results/sweep1/final_k16 \
|
|
120
|
+
--dataset data/expression.csv \
|
|
121
|
+
--output-dir results/sweep1/final_k16/modules \
|
|
122
|
+
--expr data/expression.csv \
|
|
123
|
+
--soft-eigengenes
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Export latents:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
bsvae-networks export-latents \
|
|
130
|
+
--model-path results/sweep1/final_k16 \
|
|
131
|
+
--dataset data/expression.csv \
|
|
132
|
+
--output results/sweep1/final_k16/latents
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This command writes `results/sweep1/final_k16/latents.npz` containing `mu`, `logvar`, `gamma`, and `feature_ids`.
|
|
136
|
+
|
|
137
|
+
Extract feature-feature networks:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
bsvae-networks extract-networks \
|
|
141
|
+
--model-path results/sweep1/final_k16 \
|
|
142
|
+
--dataset data/expression.csv \
|
|
143
|
+
--output-dir results/sweep1/final_k16/networks \
|
|
144
|
+
--methods mu_cosine gamma_knn \
|
|
145
|
+
--top-k 50
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Run latent-space analysis:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
bsvae-networks latent-analysis \
|
|
152
|
+
--model-path results/sweep1/final_k16 \
|
|
153
|
+
--dataset data/expression.csv \
|
|
154
|
+
--output-dir results/sweep1/final_k16/latent_analysis \
|
|
155
|
+
--kmeans-k 16 \
|
|
156
|
+
--umap
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Training Outputs
|
|
160
|
+
|
|
161
|
+
`bsvae-train` writes to `results/<experiment>/` by default:
|
|
162
|
+
|
|
163
|
+
- `model.pt`
|
|
164
|
+
- `specs.json`
|
|
165
|
+
- `train_losses.csv`
|
|
166
|
+
- `model-<epoch>.pt` checkpoints when `--checkpoint-every` is greater than zero
|
|
167
|
+
|
|
168
|
+
`bsvae-sweep-k` writes:
|
|
169
|
+
|
|
170
|
+
- `results/<name>/sweep_k/sweep_results.csv`
|
|
171
|
+
- `results/<name>/sweep_k/sweep_summary.json`
|
|
172
|
+
- `results/<name>/sweep_k/k<K>/rep_<rep>/...` per-sweep run outputs
|
|
173
|
+
- `results/<name>/final_k<K>/...` for the final retrained model when `--train-final` is enabled
|
|
174
|
+
|
|
175
|
+
## Simulation and Benchmarking
|
|
176
|
+
|
|
177
|
+
Generate one synthetic dataset:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
bsvae-simulate generate \
|
|
181
|
+
--output data/sim_expr.csv \
|
|
182
|
+
--save-ground-truth data/sim_truth.csv
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Generate a scenario grid:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
bsvae-simulate init-config --output sim.yaml
|
|
189
|
+
|
|
190
|
+
bsvae-simulate generate-grid \
|
|
191
|
+
--config sim.yaml \
|
|
192
|
+
--outdir results/sim_pub_v1 \
|
|
193
|
+
--reps 30 \
|
|
194
|
+
--base-seed 13
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Validate the generated grid:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
bsvae-simulate validate-grid --grid-dir results/sim_pub_v1
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Each scenario replicate writes method-ready files under `results/sim_pub_v1/scenarios/<scenario_id>/rep_<rep>/`, including:
|
|
204
|
+
|
|
205
|
+
- `expr/features_x_samples.tsv.gz`
|
|
206
|
+
- `expr/samples_x_features.tsv.gz`
|
|
207
|
+
- `truth/modules_hard.csv`
|
|
208
|
+
- `method_inputs.json`
|
|
209
|
+
|
|
210
|
+
## Documentation
|
|
211
|
+
|
|
212
|
+
See the docs for the full workflow and command reference:
|
|
213
|
+
|
|
214
|
+
- `docs/index.md`
|
|
215
|
+
- `docs/quickstart.md`
|
|
216
|
+
- `docs/tutorial.md`
|
|
217
|
+
- `docs/cli.md`
|
|
218
|
+
- `docs/networks.md`
|
|
219
|
+
- `docs/hyperparameters.md`
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
This project is licensed under the [GNU General Public License v3.0](LICENSE).
|
|
224
|
+
|
bsvae-0.3.2/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# BSVAE: Gaussian Mixture VAE for Module Discovery
|
|
2
|
+
|
|
3
|
+
[](https://pytorch.org/)
|
|
4
|
+
[](https://python.org/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://bsvae.readthedocs.io/)
|
|
7
|
+
|
|
8
|
+
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.
|
|
9
|
+
|
|
10
|
+
## What The Package Provides
|
|
11
|
+
|
|
12
|
+
- `bsvae-train` for training a two-phase GMM-VAE
|
|
13
|
+
- `bsvae-sweep-k` for selecting the number of modules (`K`) with held-out validation and optional stability replicates
|
|
14
|
+
- `bsvae-networks` for post-training network extraction, module extraction, latent export, and latent analysis
|
|
15
|
+
- `bsvae-simulate` for synthetic data generation, scenario-grid simulation, and module-recovery benchmarking
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Install from PyPI:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install bsvae
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Install from source:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git clone https://github.com/heart-gen/BSVAE.git
|
|
29
|
+
cd BSVAE
|
|
30
|
+
pip install -e .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Optional dependency:
|
|
34
|
+
|
|
35
|
+
- `anndata` is only needed for `.h5ad` input or `.h5ad` latent export helpers
|
|
36
|
+
|
|
37
|
+
## Input Data Contract
|
|
38
|
+
|
|
39
|
+
Training and most downstream commands expect an expression matrix in `features x samples` orientation:
|
|
40
|
+
|
|
41
|
+
- rows are features such as genes, transcripts, or proteins
|
|
42
|
+
- columns are sample IDs
|
|
43
|
+
- the first column is the feature index when using CSV/TSV
|
|
44
|
+
|
|
45
|
+
Supported loaders:
|
|
46
|
+
|
|
47
|
+
- `.csv` / `.csv.gz`
|
|
48
|
+
- `.tsv` / `.tsv.gz`
|
|
49
|
+
- `.h5` / `.hdf5`
|
|
50
|
+
- `.h5ad` with optional `anndata`
|
|
51
|
+
|
|
52
|
+
## CLI Entry Points
|
|
53
|
+
|
|
54
|
+
- `bsvae-train`
|
|
55
|
+
- `bsvae-sweep-k`
|
|
56
|
+
- `bsvae-networks`
|
|
57
|
+
- `bsvae-simulate`
|
|
58
|
+
|
|
59
|
+
## Quickstart
|
|
60
|
+
|
|
61
|
+
Train a small model:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bsvae-train pilot_run \
|
|
65
|
+
--dataset data/expression.csv \
|
|
66
|
+
--epochs 50 \
|
|
67
|
+
--n-modules 12 \
|
|
68
|
+
--latent-dim 16
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Recommended production flow: select `K` first, then use the retrained final model.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bsvae-sweep-k sweep1 \
|
|
75
|
+
--dataset data/expression.csv \
|
|
76
|
+
--k-grid 8,12,16,24,32 \
|
|
77
|
+
--sweep-epochs 60 \
|
|
78
|
+
--stability-reps 5 \
|
|
79
|
+
--val-frac 0.1
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This writes sweep outputs under `results/sweep1/sweep_k/` and retrains the selected model under `results/sweep1/final_k<K>/`.
|
|
83
|
+
|
|
84
|
+
Extract module assignments from the final model:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
bsvae-networks extract-modules \
|
|
88
|
+
--model-path results/sweep1/final_k16 \
|
|
89
|
+
--dataset data/expression.csv \
|
|
90
|
+
--output-dir results/sweep1/final_k16/modules \
|
|
91
|
+
--expr data/expression.csv \
|
|
92
|
+
--soft-eigengenes
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Export latents:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
bsvae-networks export-latents \
|
|
99
|
+
--model-path results/sweep1/final_k16 \
|
|
100
|
+
--dataset data/expression.csv \
|
|
101
|
+
--output results/sweep1/final_k16/latents
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This command writes `results/sweep1/final_k16/latents.npz` containing `mu`, `logvar`, `gamma`, and `feature_ids`.
|
|
105
|
+
|
|
106
|
+
Extract feature-feature networks:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
bsvae-networks extract-networks \
|
|
110
|
+
--model-path results/sweep1/final_k16 \
|
|
111
|
+
--dataset data/expression.csv \
|
|
112
|
+
--output-dir results/sweep1/final_k16/networks \
|
|
113
|
+
--methods mu_cosine gamma_knn \
|
|
114
|
+
--top-k 50
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Run latent-space analysis:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
bsvae-networks latent-analysis \
|
|
121
|
+
--model-path results/sweep1/final_k16 \
|
|
122
|
+
--dataset data/expression.csv \
|
|
123
|
+
--output-dir results/sweep1/final_k16/latent_analysis \
|
|
124
|
+
--kmeans-k 16 \
|
|
125
|
+
--umap
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Training Outputs
|
|
129
|
+
|
|
130
|
+
`bsvae-train` writes to `results/<experiment>/` by default:
|
|
131
|
+
|
|
132
|
+
- `model.pt`
|
|
133
|
+
- `specs.json`
|
|
134
|
+
- `train_losses.csv`
|
|
135
|
+
- `model-<epoch>.pt` checkpoints when `--checkpoint-every` is greater than zero
|
|
136
|
+
|
|
137
|
+
`bsvae-sweep-k` writes:
|
|
138
|
+
|
|
139
|
+
- `results/<name>/sweep_k/sweep_results.csv`
|
|
140
|
+
- `results/<name>/sweep_k/sweep_summary.json`
|
|
141
|
+
- `results/<name>/sweep_k/k<K>/rep_<rep>/...` per-sweep run outputs
|
|
142
|
+
- `results/<name>/final_k<K>/...` for the final retrained model when `--train-final` is enabled
|
|
143
|
+
|
|
144
|
+
## Simulation and Benchmarking
|
|
145
|
+
|
|
146
|
+
Generate one synthetic dataset:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
bsvae-simulate generate \
|
|
150
|
+
--output data/sim_expr.csv \
|
|
151
|
+
--save-ground-truth data/sim_truth.csv
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Generate a scenario grid:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
bsvae-simulate init-config --output sim.yaml
|
|
158
|
+
|
|
159
|
+
bsvae-simulate generate-grid \
|
|
160
|
+
--config sim.yaml \
|
|
161
|
+
--outdir results/sim_pub_v1 \
|
|
162
|
+
--reps 30 \
|
|
163
|
+
--base-seed 13
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Validate the generated grid:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
bsvae-simulate validate-grid --grid-dir results/sim_pub_v1
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Each scenario replicate writes method-ready files under `results/sim_pub_v1/scenarios/<scenario_id>/rep_<rep>/`, including:
|
|
173
|
+
|
|
174
|
+
- `expr/features_x_samples.tsv.gz`
|
|
175
|
+
- `expr/samples_x_features.tsv.gz`
|
|
176
|
+
- `truth/modules_hard.csv`
|
|
177
|
+
- `method_inputs.json`
|
|
178
|
+
|
|
179
|
+
## Documentation
|
|
180
|
+
|
|
181
|
+
See the docs for the full workflow and command reference:
|
|
182
|
+
|
|
183
|
+
- `docs/index.md`
|
|
184
|
+
- `docs/quickstart.md`
|
|
185
|
+
- `docs/tutorial.md`
|
|
186
|
+
- `docs/cli.md`
|
|
187
|
+
- `docs/networks.md`
|
|
188
|
+
- `docs/hyperparameters.md`
|
|
189
|
+
|
|
190
|
+
## License
|
|
191
|
+
|
|
192
|
+
This project is licensed under the [GNU General Public License v3.0](LICENSE).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "bsvae"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2" # dynamically resolved by poetry-dynamic-versioning
|
|
4
4
|
description = "Gaussian Mixture VAE for multi-modal biological module discovery in omics data."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = ["Kynon J Benjamin <kj.benjamin90@gmail.com>"]
|
|
@@ -50,6 +50,7 @@ repository = "https://github.com/heart-gen/BSVAE"
|
|
|
50
50
|
bsvae-train = "bsvae.cli.train:cli"
|
|
51
51
|
bsvae-networks = "bsvae.cli.networks:cli"
|
|
52
52
|
bsvae-simulate = "bsvae.cli.simulate:cli"
|
|
53
|
+
bsvae-sweep-k = "bsvae.cli.sweep_k:cli"
|
|
53
54
|
|
|
54
55
|
[tool.poetry.group.dev.dependencies]
|
|
55
56
|
pytest = ">=8.4.2,<9.0.0"
|
|
@@ -64,6 +64,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
64
64
|
mp.add_argument("--use-leiden", action="store_true",
|
|
65
65
|
help="Also run Leiden clustering as fallback comparison.")
|
|
66
66
|
mp.add_argument("--leiden-resolution", type=float, default=1.0)
|
|
67
|
+
mp.add_argument("--tx2gene", type=str, default=None,
|
|
68
|
+
help="TSV (transcript_id, gene_id) for isoform-to-gene aggregation.")
|
|
69
|
+
mp.add_argument("--aggregate-to-gene", action="store_true", default=False,
|
|
70
|
+
help="Aggregate isoform-level γ to gene level by averaging across "
|
|
71
|
+
"isoforms per gene. Saves gamma_gene.npz and "
|
|
72
|
+
"hard_assignments_gene.npz. Requires --tx2gene.")
|
|
67
73
|
|
|
68
74
|
# ------------------------------------------------------------------
|
|
69
75
|
# export-latents
|
|
@@ -120,6 +126,35 @@ def handle_extract_networks(args, logger: logging.Logger) -> None:
|
|
|
120
126
|
logger.info("Network extraction complete → %s", args.output_dir)
|
|
121
127
|
|
|
122
128
|
|
|
129
|
+
def _aggregate_gamma_to_gene(
|
|
130
|
+
gamma: "np.ndarray",
|
|
131
|
+
feature_ids: List[str],
|
|
132
|
+
tx2gene_path: str,
|
|
133
|
+
) -> "tuple[np.ndarray, list[str]]":
|
|
134
|
+
"""Average isoform-level γ (F_tx, K) to gene level, renormalise rows to sum 1."""
|
|
135
|
+
import numpy as np
|
|
136
|
+
from collections import defaultdict
|
|
137
|
+
from bsvae.utils.hierarchy import load_tx2gene
|
|
138
|
+
|
|
139
|
+
tx2gene = load_tx2gene(path=tx2gene_path, feature_ids=feature_ids)
|
|
140
|
+
# Map every feature to its gene_id; fall back to itself for unmapped features
|
|
141
|
+
fid_to_gene = {fid: tx2gene.get(fid, fid) for fid in feature_ids}
|
|
142
|
+
|
|
143
|
+
gene_to_idxs: dict = defaultdict(list)
|
|
144
|
+
for i, fid in enumerate(feature_ids):
|
|
145
|
+
gene_to_idxs[fid_to_gene[fid]].append(i)
|
|
146
|
+
|
|
147
|
+
gene_ids = list(gene_to_idxs.keys())
|
|
148
|
+
gamma_gene = np.zeros((len(gene_ids), gamma.shape[1]), dtype=np.float32)
|
|
149
|
+
for j, gid in enumerate(gene_ids):
|
|
150
|
+
gamma_gene[j] = gamma[gene_to_idxs[gid]].mean(axis=0)
|
|
151
|
+
|
|
152
|
+
row_sums = gamma_gene.sum(axis=1, keepdims=True)
|
|
153
|
+
row_sums = np.where(row_sums > 0, row_sums, 1.0)
|
|
154
|
+
gamma_gene /= row_sums
|
|
155
|
+
return gamma_gene, gene_ids
|
|
156
|
+
|
|
157
|
+
|
|
123
158
|
def handle_extract_modules(args, logger: logging.Logger) -> None:
|
|
124
159
|
import numpy as np
|
|
125
160
|
from bsvae.utils.modelIO import load_model
|
|
@@ -144,6 +179,30 @@ def handle_extract_modules(args, logger: logging.Logger) -> None:
|
|
|
144
179
|
model.n_modules, len(feature_ids),
|
|
145
180
|
)
|
|
146
181
|
|
|
182
|
+
if args.aggregate_to_gene:
|
|
183
|
+
if not args.tx2gene:
|
|
184
|
+
logger.warning("--aggregate-to-gene requires --tx2gene; skipping.")
|
|
185
|
+
else:
|
|
186
|
+
gamma_gene, gene_ids = _aggregate_gamma_to_gene(
|
|
187
|
+
result["gamma"], result["feature_ids"], args.tx2gene
|
|
188
|
+
)
|
|
189
|
+
hard_gene = gamma_gene.argmax(axis=1).astype(np.int32)
|
|
190
|
+
out = Path(args.output_dir)
|
|
191
|
+
np.savez_compressed(
|
|
192
|
+
out / "gamma_gene.npz",
|
|
193
|
+
gamma=gamma_gene,
|
|
194
|
+
feature_ids=np.array(gene_ids, dtype=object),
|
|
195
|
+
)
|
|
196
|
+
np.savez_compressed(
|
|
197
|
+
out / "hard_assignments_gene.npz",
|
|
198
|
+
hard_assignments=hard_gene,
|
|
199
|
+
feature_ids=np.array(gene_ids, dtype=object),
|
|
200
|
+
)
|
|
201
|
+
logger.info(
|
|
202
|
+
"Gene-level aggregation: %d transcripts → %d genes",
|
|
203
|
+
len(result["feature_ids"]), len(gene_ids),
|
|
204
|
+
)
|
|
205
|
+
|
|
147
206
|
if args.soft_eigengenes and args.expr:
|
|
148
207
|
import pandas as pd
|
|
149
208
|
expr = pd.read_csv(args.expr, index_col=0, sep="\t" if args.expr.endswith(".tsv") else ",")
|
|
@@ -3,8 +3,12 @@ bsvae-simulate — Generate synthetic omics data and benchmark module methods.
|
|
|
3
3
|
|
|
4
4
|
Subcommands
|
|
5
5
|
-----------
|
|
6
|
-
generate
|
|
7
|
-
benchmark
|
|
6
|
+
generate Simulate omics data with known block-diagonal module structure.
|
|
7
|
+
benchmark ARI/NMI/enrichment vs WGCNA/GNVAE on simulated data.
|
|
8
|
+
init-config Write starter simulation grid config.
|
|
9
|
+
generate-grid Generate all scenarios/replicates defined in config.
|
|
10
|
+
generate-scenario Generate one scenario/replicate.
|
|
11
|
+
validate-grid Validate generated grid output structure.
|
|
8
12
|
"""
|
|
9
13
|
|
|
10
14
|
from __future__ import annotations
|
|
@@ -43,7 +47,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
43
47
|
gp.add_argument("--between-corr", type=float, default=0.0,
|
|
44
48
|
help="Between-module correlation (default: 0.0).")
|
|
45
49
|
gp.add_argument("--noise-std", type=float, default=0.2)
|
|
46
|
-
gp.add_argument("--seed", type=int, default=
|
|
50
|
+
gp.add_argument("--seed", type=int, default=13)
|
|
47
51
|
gp.add_argument("--save-ground-truth", help="Path to save ground-truth module labels CSV.")
|
|
48
52
|
|
|
49
53
|
# ------------------------------------------------------------------
|
|
@@ -57,6 +61,37 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
57
61
|
bp.add_argument("--batch-size", type=int, default=128)
|
|
58
62
|
bp.add_argument("--no-cuda", action="store_true", default=False)
|
|
59
63
|
|
|
64
|
+
# ------------------------------------------------------------------
|
|
65
|
+
# init-config
|
|
66
|
+
# ------------------------------------------------------------------
|
|
67
|
+
cp = subs.add_parser("init-config", help="Write a starter simulation config file.")
|
|
68
|
+
cp.add_argument("--output", default="sim.yaml", help="Destination config path.")
|
|
69
|
+
|
|
70
|
+
# ------------------------------------------------------------------
|
|
71
|
+
# generate-grid
|
|
72
|
+
# ------------------------------------------------------------------
|
|
73
|
+
gp2 = subs.add_parser("generate-grid", help="Generate a scenario grid from config.")
|
|
74
|
+
gp2.add_argument("--config", required=True, help="Simulation config path.")
|
|
75
|
+
gp2.add_argument("--outdir", required=True, help="Output directory for scenario grid.")
|
|
76
|
+
gp2.add_argument("--reps", type=int, default=None, help="Override replicates per scenario.")
|
|
77
|
+
gp2.add_argument("--base-seed", type=int, default=None, help="Override base seed for all runs.")
|
|
78
|
+
|
|
79
|
+
# ------------------------------------------------------------------
|
|
80
|
+
# generate-scenario
|
|
81
|
+
# ------------------------------------------------------------------
|
|
82
|
+
sp = subs.add_parser("generate-scenario", help="Generate a single scenario/replicate from config.")
|
|
83
|
+
sp.add_argument("--config", required=True, help="Simulation config path.")
|
|
84
|
+
sp.add_argument("--scenario-id", required=True, help="Scenario identifier from config expansion.")
|
|
85
|
+
sp.add_argument("--rep", type=int, default=0, help="Replicate index.")
|
|
86
|
+
sp.add_argument("--outdir", required=True, help="Output directory for scenario grid.")
|
|
87
|
+
sp.add_argument("--base-seed", type=int, default=None, help="Override base seed for this run.")
|
|
88
|
+
|
|
89
|
+
# ------------------------------------------------------------------
|
|
90
|
+
# validate-grid
|
|
91
|
+
# ------------------------------------------------------------------
|
|
92
|
+
vp = subs.add_parser("validate-grid", help="Validate scenario grid output files.")
|
|
93
|
+
vp.add_argument("--grid-dir", required=True, help="Scenario grid output directory.")
|
|
94
|
+
|
|
60
95
|
return parser
|
|
61
96
|
|
|
62
97
|
|
|
@@ -144,6 +179,58 @@ def handle_benchmark(args, logger: logging.Logger) -> None:
|
|
|
144
179
|
logger.info("Benchmark metrics saved to %s", out)
|
|
145
180
|
|
|
146
181
|
|
|
182
|
+
def handle_init_config(args, logger: logging.Logger) -> None:
|
|
183
|
+
from bsvae.simulation.scenario import write_starter_config
|
|
184
|
+
|
|
185
|
+
write_starter_config(args.output)
|
|
186
|
+
logger.info("Starter simulation config written to %s", args.output)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def handle_generate_grid(args, logger: logging.Logger) -> None:
|
|
190
|
+
from bsvae.simulation.scenario import load_config, generate_grid
|
|
191
|
+
|
|
192
|
+
config = load_config(args.config)
|
|
193
|
+
summary = generate_grid(
|
|
194
|
+
config=config,
|
|
195
|
+
outdir=args.outdir,
|
|
196
|
+
reps=args.reps,
|
|
197
|
+
base_seed=args.base_seed,
|
|
198
|
+
)
|
|
199
|
+
logger.info(
|
|
200
|
+
"Grid complete: %d scenarios × %d reps = %d runs",
|
|
201
|
+
summary["n_scenarios"],
|
|
202
|
+
summary["n_reps"],
|
|
203
|
+
summary["n_runs"],
|
|
204
|
+
)
|
|
205
|
+
logger.info("Manifest: %s", summary["manifest"])
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def handle_generate_scenario(args, logger: logging.Logger) -> None:
|
|
209
|
+
from bsvae.simulation.scenario import load_config, generate_scenario
|
|
210
|
+
|
|
211
|
+
config = load_config(args.config)
|
|
212
|
+
run_dir = generate_scenario(
|
|
213
|
+
config=config,
|
|
214
|
+
scenario_id=args.scenario_id,
|
|
215
|
+
rep=args.rep,
|
|
216
|
+
outdir=args.outdir,
|
|
217
|
+
base_seed=args.base_seed,
|
|
218
|
+
)
|
|
219
|
+
logger.info("Scenario generated: %s", run_dir)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def handle_validate_grid(args, logger: logging.Logger) -> None:
|
|
223
|
+
from bsvae.simulation.scenario import validate_grid
|
|
224
|
+
|
|
225
|
+
result = validate_grid(args.grid_dir)
|
|
226
|
+
logger.info(
|
|
227
|
+
"Grid validation: checked %d/%d rows, all required files present=%s",
|
|
228
|
+
result["checked_rows"],
|
|
229
|
+
result["manifest_rows"],
|
|
230
|
+
result["all_required_files_present"],
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
147
234
|
def cli(argv=None):
|
|
148
235
|
parser = build_parser()
|
|
149
236
|
args = parser.parse_args(argv)
|
|
@@ -153,6 +240,14 @@ def cli(argv=None):
|
|
|
153
240
|
handle_generate(args, logger)
|
|
154
241
|
elif args.command == "benchmark":
|
|
155
242
|
handle_benchmark(args, logger)
|
|
243
|
+
elif args.command == "init-config":
|
|
244
|
+
handle_init_config(args, logger)
|
|
245
|
+
elif args.command == "generate-grid":
|
|
246
|
+
handle_generate_grid(args, logger)
|
|
247
|
+
elif args.command == "generate-scenario":
|
|
248
|
+
handle_generate_scenario(args, logger)
|
|
249
|
+
elif args.command == "validate-grid":
|
|
250
|
+
handle_validate_grid(args, logger)
|
|
156
251
|
else:
|
|
157
252
|
parser.error(f"Unknown command: {args.command}")
|
|
158
253
|
|