babappaomega 0.1.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.
- babappaomega-0.1.0/PKG-INFO +75 -0
- babappaomega-0.1.0/README.md +60 -0
- babappaomega-0.1.0/babappaomega/__init__.py +1 -0
- babappaomega-0.1.0/babappaomega/cli.py +31 -0
- babappaomega-0.1.0/babappaomega/encoding.py +23 -0
- babappaomega-0.1.0/babappaomega/inference.py +184 -0
- babappaomega-0.1.0/babappaomega/models.py +52 -0
- babappaomega-0.1.0/babappaomega/tree.py +27 -0
- babappaomega-0.1.0/babappaomega/utils.py +25 -0
- babappaomega-0.1.0/babappaomega.egg-info/PKG-INFO +75 -0
- babappaomega-0.1.0/babappaomega.egg-info/SOURCES.txt +15 -0
- babappaomega-0.1.0/babappaomega.egg-info/dependency_links.txt +1 -0
- babappaomega-0.1.0/babappaomega.egg-info/entry_points.txt +2 -0
- babappaomega-0.1.0/babappaomega.egg-info/requires.txt +6 -0
- babappaomega-0.1.0/babappaomega.egg-info/top_level.txt +1 -0
- babappaomega-0.1.0/pyproject.toml +28 -0
- babappaomega-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: babappaomega
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: BABAPPAΩ: Likelihood-free branch–site inference of episodic positive selection
|
|
5
|
+
Author: Krishnendu Sinha
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: torch>=2.0
|
|
10
|
+
Requires-Dist: numpy
|
|
11
|
+
Requires-Dist: biopython
|
|
12
|
+
Requires-Dist: ete3
|
|
13
|
+
Requires-Dist: six
|
|
14
|
+
Requires-Dist: platformdirs
|
|
15
|
+
|
|
16
|
+
# BABAPPAΩ
|
|
17
|
+
|
|
18
|
+
BABAPPAΩ is a mechanistically grounded engine for branch–site inference of
|
|
19
|
+
episodic positive selection.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
```bash
|
|
23
|
+
pip install babappaomega
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
b```abappaomega predict \
|
|
27
|
+
--alignment alignment.fasta \
|
|
28
|
+
--tree tree.nwk \
|
|
29
|
+
--out results.json```
|
|
30
|
+
|
|
31
|
+
## Model weights
|
|
32
|
+
|
|
33
|
+
The frozen reference model for BABAPPAΩ is archived on Zenodo:
|
|
34
|
+
|
|
35
|
+
DOI: 10.5281/zenodo.18195868
|
|
36
|
+
|
|
37
|
+
On first use, the model is downloaded automatically, verified using the
|
|
38
|
+
archival MD5 checksum, cached locally, and reused for all subsequent runs.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
# 1️⃣1️⃣ `LICENSE`
|
|
43
|
+
|
|
44
|
+
Use **MIT** or **BSD-3-Clause** (recommended).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 🔒 FINAL STATUS
|
|
49
|
+
|
|
50
|
+
You now have:
|
|
51
|
+
|
|
52
|
+
- ✅ A **real CLI**
|
|
53
|
+
- ✅ PyPI-ready packaging
|
|
54
|
+
- ✅ Frozen model bundled correctly
|
|
55
|
+
- ✅ GPU-first inference
|
|
56
|
+
- ✅ Safe future swap for 1M model
|
|
57
|
+
- ✅ Reviewer-safe distribution story
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Next (recommended)
|
|
62
|
+
1. Dry-run: `pip install . && babappaomega predict …`
|
|
63
|
+
2. Create a **GitHub release**
|
|
64
|
+
3. Upload to **TestPyPI**
|
|
65
|
+
4. Then real PyPI
|
|
66
|
+
|
|
67
|
+
If you want, next I can:
|
|
68
|
+
- validate model I/O expectations
|
|
69
|
+
- write automated tests
|
|
70
|
+
- create conda recipe
|
|
71
|
+
- prepare PyPI release checklist
|
|
72
|
+
|
|
73
|
+
Just say which.
|
|
74
|
+
|
|
75
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# BABAPPAΩ
|
|
2
|
+
|
|
3
|
+
BABAPPAΩ is a mechanistically grounded engine for branch–site inference of
|
|
4
|
+
episodic positive selection.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
```bash
|
|
8
|
+
pip install babappaomega
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
b```abappaomega predict \
|
|
12
|
+
--alignment alignment.fasta \
|
|
13
|
+
--tree tree.nwk \
|
|
14
|
+
--out results.json```
|
|
15
|
+
|
|
16
|
+
## Model weights
|
|
17
|
+
|
|
18
|
+
The frozen reference model for BABAPPAΩ is archived on Zenodo:
|
|
19
|
+
|
|
20
|
+
DOI: 10.5281/zenodo.18195868
|
|
21
|
+
|
|
22
|
+
On first use, the model is downloaded automatically, verified using the
|
|
23
|
+
archival MD5 checksum, cached locally, and reused for all subsequent runs.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# 1️⃣1️⃣ `LICENSE`
|
|
28
|
+
|
|
29
|
+
Use **MIT** or **BSD-3-Clause** (recommended).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 🔒 FINAL STATUS
|
|
34
|
+
|
|
35
|
+
You now have:
|
|
36
|
+
|
|
37
|
+
- ✅ A **real CLI**
|
|
38
|
+
- ✅ PyPI-ready packaging
|
|
39
|
+
- ✅ Frozen model bundled correctly
|
|
40
|
+
- ✅ GPU-first inference
|
|
41
|
+
- ✅ Safe future swap for 1M model
|
|
42
|
+
- ✅ Reviewer-safe distribution story
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
### Next (recommended)
|
|
47
|
+
1. Dry-run: `pip install . && babappaomega predict …`
|
|
48
|
+
2. Create a **GitHub release**
|
|
49
|
+
3. Upload to **TestPyPI**
|
|
50
|
+
4. Then real PyPI
|
|
51
|
+
|
|
52
|
+
If you want, next I can:
|
|
53
|
+
- validate model I/O expectations
|
|
54
|
+
- write automated tests
|
|
55
|
+
- create conda recipe
|
|
56
|
+
- prepare PyPI release checklist
|
|
57
|
+
|
|
58
|
+
Just say which.
|
|
59
|
+
|
|
60
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from babappaomega.inference import run_inference
|
|
3
|
+
|
|
4
|
+
def main():
|
|
5
|
+
parser = argparse.ArgumentParser(
|
|
6
|
+
prog="babappaomega",
|
|
7
|
+
description="BABAPPAΩ: episodic branch–site selection inference"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
11
|
+
|
|
12
|
+
p = sub.add_parser("predict", help="Run inference on an alignment")
|
|
13
|
+
p.add_argument("--alignment", required=True, help="Codon alignment (FASTA)")
|
|
14
|
+
p.add_argument("--tree", required=True, help="Phylogenetic tree (Newick)")
|
|
15
|
+
p.add_argument("--out", required=True, help="Output JSON file")
|
|
16
|
+
p.add_argument("--device", default="auto", choices=["auto", "cpu", "cuda"])
|
|
17
|
+
p.add_argument("--model", default="frozen")
|
|
18
|
+
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
|
|
21
|
+
if args.command == "predict":
|
|
22
|
+
run_inference(
|
|
23
|
+
alignment_path=args.alignment,
|
|
24
|
+
tree_path=args.tree,
|
|
25
|
+
out_path=args.out,
|
|
26
|
+
device=args.device,
|
|
27
|
+
model_tag=args.model,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
main()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from Bio import SeqIO
|
|
3
|
+
|
|
4
|
+
CODONS = [
|
|
5
|
+
a+b+c for a in "ACGT" for b in "ACGT" for c in "ACGT"
|
|
6
|
+
if a+b+c not in ["TAA", "TAG", "TGA"]
|
|
7
|
+
]
|
|
8
|
+
CODON_TO_ID = {c: i for i, c in enumerate(CODONS)}
|
|
9
|
+
|
|
10
|
+
def encode_alignment(fasta_path):
|
|
11
|
+
records = list(SeqIO.parse(fasta_path, "fasta"))
|
|
12
|
+
ntaxa = len(records)
|
|
13
|
+
seq_len = len(records[0].seq) // 3
|
|
14
|
+
|
|
15
|
+
tensor = np.zeros((ntaxa, seq_len), dtype=np.int64)
|
|
16
|
+
|
|
17
|
+
for i, rec in enumerate(records):
|
|
18
|
+
seq = str(rec.seq)
|
|
19
|
+
for j in range(seq_len):
|
|
20
|
+
codon = seq[3*j:3*j+3]
|
|
21
|
+
tensor[i, j] = CODON_TO_ID.get(codon, 0)
|
|
22
|
+
|
|
23
|
+
return tensor, ntaxa, seq_len
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import csv
|
|
3
|
+
import os
|
|
4
|
+
import torch
|
|
5
|
+
import numpy as np
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
from babappaomega.utils import resolve_device
|
|
9
|
+
from babappaomega.encoding import encode_alignment
|
|
10
|
+
from babappaomega.tree import load_tree, enumerate_branches
|
|
11
|
+
from babappaomega.models import ensure_model
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_model(model_tag: str, device: torch.device):
|
|
15
|
+
"""
|
|
16
|
+
Load TorchScript BABAPPAΩ model from Zenodo.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
if model_tag != "frozen":
|
|
20
|
+
raise ValueError(
|
|
21
|
+
f"Model '{model_tag}' is not available. "
|
|
22
|
+
"Only the frozen reference model is supported."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
model_path = ensure_model(model_tag)
|
|
26
|
+
|
|
27
|
+
model = torch.jit.load(model_path, map_location=device)
|
|
28
|
+
model.eval()
|
|
29
|
+
|
|
30
|
+
return model
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@torch.no_grad()
|
|
35
|
+
def run_inference(
|
|
36
|
+
alignment_path: str,
|
|
37
|
+
tree_path: str,
|
|
38
|
+
out_path: str,
|
|
39
|
+
device: str = "auto",
|
|
40
|
+
model_tag: str = "frozen",
|
|
41
|
+
):
|
|
42
|
+
"""
|
|
43
|
+
Run BABAPPAΩ inference on a codon alignment and phylogenetic tree.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
# -------------------------
|
|
47
|
+
# Device resolution
|
|
48
|
+
# -------------------------
|
|
49
|
+
device = resolve_device(device)
|
|
50
|
+
|
|
51
|
+
# -------------------------
|
|
52
|
+
# Load model
|
|
53
|
+
# -------------------------
|
|
54
|
+
model = load_model(model_tag, device)
|
|
55
|
+
|
|
56
|
+
# -------------------------
|
|
57
|
+
# Encode inputs
|
|
58
|
+
# -------------------------
|
|
59
|
+
X, ntaxa, L = encode_alignment(alignment_path)
|
|
60
|
+
tree = load_tree(tree_path)
|
|
61
|
+
branches = enumerate_branches(tree)
|
|
62
|
+
|
|
63
|
+
X = torch.tensor(X, dtype=torch.long, device=device).unsqueeze(0)
|
|
64
|
+
|
|
65
|
+
if device.type == "cpu" and ntaxa > 120:
|
|
66
|
+
print(
|
|
67
|
+
"[BABAPPAΩ WARNING] Large number of taxa detected "
|
|
68
|
+
f"(n={ntaxa}). GPU acceleration is strongly recommended."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# -------------------------
|
|
72
|
+
# Forward pass (per-branch)
|
|
73
|
+
# -------------------------
|
|
74
|
+
n_branches = len(branches)
|
|
75
|
+
|
|
76
|
+
# --- Run ONCE to determine n_regimes ---
|
|
77
|
+
branch_mask = torch.zeros(
|
|
78
|
+
(1, n_branches),
|
|
79
|
+
dtype=torch.long,
|
|
80
|
+
device=device,
|
|
81
|
+
)
|
|
82
|
+
branch_mask[0, 0] = 1
|
|
83
|
+
|
|
84
|
+
outputs = model(X, branch_mask)
|
|
85
|
+
|
|
86
|
+
det_example, regime_example, _ = outputs
|
|
87
|
+
|
|
88
|
+
det_example = det_example.detach().cpu().numpy()[0]
|
|
89
|
+
regime_example = regime_example.detach().cpu().numpy()[0]
|
|
90
|
+
|
|
91
|
+
n_regimes = regime_example.shape[-1]
|
|
92
|
+
|
|
93
|
+
# --- Allocate matrices ---
|
|
94
|
+
det_matrix = np.zeros((n_branches, L), dtype=float)
|
|
95
|
+
regime_matrix = np.zeros((n_branches, L, n_regimes), dtype=float)
|
|
96
|
+
|
|
97
|
+
# --- Fill matrices ---
|
|
98
|
+
for b in range(n_branches):
|
|
99
|
+
branch_mask.zero_()
|
|
100
|
+
branch_mask[0, b] = 1
|
|
101
|
+
|
|
102
|
+
outputs = model(X, branch_mask)
|
|
103
|
+
det, regime, _ = outputs
|
|
104
|
+
|
|
105
|
+
det = torch.sigmoid(det).detach().cpu().numpy()[0]
|
|
106
|
+
regime = regime.detach().cpu().numpy()[0]
|
|
107
|
+
|
|
108
|
+
det_matrix[b] = det[b]
|
|
109
|
+
regime_matrix[b] = regime[b]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# -------------------------
|
|
113
|
+
# Assemble results (FINAL)
|
|
114
|
+
# -------------------------
|
|
115
|
+
results = []
|
|
116
|
+
for b, branch in enumerate(branches):
|
|
117
|
+
for site in range(L):
|
|
118
|
+
ep = det_matrix[b, site]
|
|
119
|
+
regime_probs = regime_matrix[b, site]
|
|
120
|
+
|
|
121
|
+
regime_idx = int(np.argmax(regime_probs))
|
|
122
|
+
rp = float(np.max(regime_probs))
|
|
123
|
+
|
|
124
|
+
results.append(
|
|
125
|
+
{
|
|
126
|
+
"branch": branch,
|
|
127
|
+
"site": site + 1,
|
|
128
|
+
"episodic_probability": round(float(ep), 6),
|
|
129
|
+
"regime": regime_idx,
|
|
130
|
+
"regime_probability": round(rp, 6),
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# -------------------------
|
|
136
|
+
# Metadata (LOCKED)
|
|
137
|
+
# -------------------------
|
|
138
|
+
metadata = {
|
|
139
|
+
"engine": "BABAPPAΩ",
|
|
140
|
+
"model": model_tag,
|
|
141
|
+
"device": device.type,
|
|
142
|
+
"ntaxa": ntaxa,
|
|
143
|
+
"sites": L,
|
|
144
|
+
"n_branches": len(branches),
|
|
145
|
+
"timestamp_utc": datetime.utcnow().isoformat() + "Z",
|
|
146
|
+
"model_source": "Zenodo",
|
|
147
|
+
"model_doi": "10.5281/zenodo.18195868",
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# -------------------------
|
|
151
|
+
# Write output
|
|
152
|
+
# -------------------------
|
|
153
|
+
ext = os.path.splitext(out_path)[1].lower()
|
|
154
|
+
|
|
155
|
+
if ext == ".json":
|
|
156
|
+
with open(out_path, "w") as f:
|
|
157
|
+
json.dump(
|
|
158
|
+
{"metadata": metadata, "results": results},
|
|
159
|
+
f,
|
|
160
|
+
indent=2,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
elif ext in {".csv", ".tsv"}:
|
|
164
|
+
delimiter = "," if ext == ".csv" else "\t"
|
|
165
|
+
with open(out_path, "w", newline="") as f:
|
|
166
|
+
writer = csv.DictWriter(
|
|
167
|
+
f,
|
|
168
|
+
fieldnames=[
|
|
169
|
+
"branch",
|
|
170
|
+
"site",
|
|
171
|
+
"episodic_probability",
|
|
172
|
+
"regime",
|
|
173
|
+
"regime_probability",
|
|
174
|
+
],
|
|
175
|
+
delimiter=delimiter,
|
|
176
|
+
)
|
|
177
|
+
writer.writeheader()
|
|
178
|
+
writer.writerows(results)
|
|
179
|
+
|
|
180
|
+
else:
|
|
181
|
+
raise ValueError(
|
|
182
|
+
f"Unsupported output format '{ext}'. "
|
|
183
|
+
"Use .json, .csv, or .tsv"
|
|
184
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import urllib.request
|
|
4
|
+
|
|
5
|
+
from platformdirs import user_cache_dir
|
|
6
|
+
|
|
7
|
+
ZENODO_MODELS = {
|
|
8
|
+
"frozen": {
|
|
9
|
+
"url": "https://zenodo.org/record/18195869/files/BABAPPAomega_frozen.pt",
|
|
10
|
+
"md5": "610280486be2c16fe0709d4e9ad7e28c",
|
|
11
|
+
"doi": "10.5281/zenodo.18195869"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def get_cache_dir():
|
|
16
|
+
cache = Path(user_cache_dir("babappaomega"))
|
|
17
|
+
cache.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
return cache
|
|
19
|
+
|
|
20
|
+
def md5sum(path):
|
|
21
|
+
h = hashlib.md5()
|
|
22
|
+
with open(path, "rb") as f:
|
|
23
|
+
for block in iter(lambda: f.read(8192), b""):
|
|
24
|
+
h.update(block)
|
|
25
|
+
return h.hexdigest()
|
|
26
|
+
|
|
27
|
+
def ensure_model(model_tag="frozen"):
|
|
28
|
+
if model_tag not in ZENODO_MODELS:
|
|
29
|
+
raise ValueError(f"Unknown model tag: {model_tag}")
|
|
30
|
+
|
|
31
|
+
entry = ZENODO_MODELS[model_tag]
|
|
32
|
+
cache_dir = get_cache_dir()
|
|
33
|
+
model_path = cache_dir / f"BABAPPAomega_{model_tag}.pt"
|
|
34
|
+
|
|
35
|
+
if model_path.exists():
|
|
36
|
+
if md5sum(model_path) == entry["md5"]:
|
|
37
|
+
return model_path
|
|
38
|
+
else:
|
|
39
|
+
model_path.unlink()
|
|
40
|
+
|
|
41
|
+
print(
|
|
42
|
+
f"[BABAPPAΩ] Downloading model '{model_tag}' from Zenodo "
|
|
43
|
+
f"(DOI: {entry['doi']})"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
urllib.request.urlretrieve(entry["url"], model_path)
|
|
47
|
+
|
|
48
|
+
if md5sum(model_path) != entry["md5"]:
|
|
49
|
+
model_path.unlink()
|
|
50
|
+
raise RuntimeError("Model download failed MD5 verification")
|
|
51
|
+
|
|
52
|
+
return model_path
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
def load_tree(tree_path):
|
|
2
|
+
"""
|
|
3
|
+
Load a phylogenetic tree from Newick format.
|
|
4
|
+
|
|
5
|
+
ete3 is imported lazily to avoid unnecessary dependencies
|
|
6
|
+
during CLI startup.
|
|
7
|
+
"""
|
|
8
|
+
try:
|
|
9
|
+
from ete3 import Tree
|
|
10
|
+
except ImportError as e:
|
|
11
|
+
raise ImportError(
|
|
12
|
+
"The 'ete3' package is required for tree handling. "
|
|
13
|
+
"Install it via: pip install ete3"
|
|
14
|
+
) from e
|
|
15
|
+
|
|
16
|
+
return Tree(tree_path, format=1)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def enumerate_branches(tree):
|
|
20
|
+
"""
|
|
21
|
+
Enumerate non-root branches in a stable traversal order.
|
|
22
|
+
"""
|
|
23
|
+
branches = []
|
|
24
|
+
for node in tree.traverse():
|
|
25
|
+
if not node.is_root():
|
|
26
|
+
branches.append(node.name or f"node_{id(node)}")
|
|
27
|
+
return branches
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import json
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
|
|
5
|
+
def resolve_device(requested="auto"):
|
|
6
|
+
if requested == "cuda":
|
|
7
|
+
if not torch.cuda.is_available():
|
|
8
|
+
raise RuntimeError("CUDA requested but not available.")
|
|
9
|
+
return torch.device("cuda")
|
|
10
|
+
|
|
11
|
+
if requested == "cpu":
|
|
12
|
+
return torch.device("cpu")
|
|
13
|
+
|
|
14
|
+
if torch.cuda.is_available():
|
|
15
|
+
return torch.device("cuda")
|
|
16
|
+
|
|
17
|
+
return torch.device("cpu")
|
|
18
|
+
|
|
19
|
+
def get_model_path(filename):
|
|
20
|
+
return files("babappaomega.assets.models") / filename
|
|
21
|
+
|
|
22
|
+
def load_metadata():
|
|
23
|
+
path = files("babappaomega.assets") / "metadata.json"
|
|
24
|
+
with open(path) as f:
|
|
25
|
+
return json.load(f)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: babappaomega
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: BABAPPAΩ: Likelihood-free branch–site inference of episodic positive selection
|
|
5
|
+
Author: Krishnendu Sinha
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: torch>=2.0
|
|
10
|
+
Requires-Dist: numpy
|
|
11
|
+
Requires-Dist: biopython
|
|
12
|
+
Requires-Dist: ete3
|
|
13
|
+
Requires-Dist: six
|
|
14
|
+
Requires-Dist: platformdirs
|
|
15
|
+
|
|
16
|
+
# BABAPPAΩ
|
|
17
|
+
|
|
18
|
+
BABAPPAΩ is a mechanistically grounded engine for branch–site inference of
|
|
19
|
+
episodic positive selection.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
```bash
|
|
23
|
+
pip install babappaomega
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
b```abappaomega predict \
|
|
27
|
+
--alignment alignment.fasta \
|
|
28
|
+
--tree tree.nwk \
|
|
29
|
+
--out results.json```
|
|
30
|
+
|
|
31
|
+
## Model weights
|
|
32
|
+
|
|
33
|
+
The frozen reference model for BABAPPAΩ is archived on Zenodo:
|
|
34
|
+
|
|
35
|
+
DOI: 10.5281/zenodo.18195868
|
|
36
|
+
|
|
37
|
+
On first use, the model is downloaded automatically, verified using the
|
|
38
|
+
archival MD5 checksum, cached locally, and reused for all subsequent runs.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
# 1️⃣1️⃣ `LICENSE`
|
|
43
|
+
|
|
44
|
+
Use **MIT** or **BSD-3-Clause** (recommended).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 🔒 FINAL STATUS
|
|
49
|
+
|
|
50
|
+
You now have:
|
|
51
|
+
|
|
52
|
+
- ✅ A **real CLI**
|
|
53
|
+
- ✅ PyPI-ready packaging
|
|
54
|
+
- ✅ Frozen model bundled correctly
|
|
55
|
+
- ✅ GPU-first inference
|
|
56
|
+
- ✅ Safe future swap for 1M model
|
|
57
|
+
- ✅ Reviewer-safe distribution story
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Next (recommended)
|
|
62
|
+
1. Dry-run: `pip install . && babappaomega predict …`
|
|
63
|
+
2. Create a **GitHub release**
|
|
64
|
+
3. Upload to **TestPyPI**
|
|
65
|
+
4. Then real PyPI
|
|
66
|
+
|
|
67
|
+
If you want, next I can:
|
|
68
|
+
- validate model I/O expectations
|
|
69
|
+
- write automated tests
|
|
70
|
+
- create conda recipe
|
|
71
|
+
- prepare PyPI release checklist
|
|
72
|
+
|
|
73
|
+
Just say which.
|
|
74
|
+
|
|
75
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
babappaomega/__init__.py
|
|
4
|
+
babappaomega/cli.py
|
|
5
|
+
babappaomega/encoding.py
|
|
6
|
+
babappaomega/inference.py
|
|
7
|
+
babappaomega/models.py
|
|
8
|
+
babappaomega/tree.py
|
|
9
|
+
babappaomega/utils.py
|
|
10
|
+
babappaomega.egg-info/PKG-INFO
|
|
11
|
+
babappaomega.egg-info/SOURCES.txt
|
|
12
|
+
babappaomega.egg-info/dependency_links.txt
|
|
13
|
+
babappaomega.egg-info/entry_points.txt
|
|
14
|
+
babappaomega.egg-info/requires.txt
|
|
15
|
+
babappaomega.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
babappaomega
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "babappaomega"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "BABAPPAΩ: Likelihood-free branch–site inference of episodic positive selection"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Krishnendu Sinha" }]
|
|
13
|
+
|
|
14
|
+
dependencies = [
|
|
15
|
+
"torch>=2.0",
|
|
16
|
+
"numpy",
|
|
17
|
+
"biopython",
|
|
18
|
+
"ete3",
|
|
19
|
+
"six",
|
|
20
|
+
"platformdirs"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
babappaomega = "babappaomega.cli:main"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools]
|
|
27
|
+
packages = ["babappaomega"]
|
|
28
|
+
|