Skip to content

Commit 5d70724

Browse files
committed
Fix bugs, update README for beta, clean up build artifacts
- Fix perm_to_dist[5] summing to 4/3 instead of 1 in discretizer.py - Fix emission_prob normalization operator precedence bug in DTO and KMeans - Fix psi validation (np.all -> np.any) catching any negative entry - Fix limited_int isinstance check that always failed - Remove unused --num-classes-min/max args and dead comments in phlag.py - Update README: new CLI flags, expanded toy example, hyperparameter table - Add tensorflow-probability to requirements.txt - Delete build.sh and setup.py (qqs.pyx replaced by pure Python qqs.py) - Add TODO.md with remaining issues Restructure as installable Python package for PyPI - Move modules into phlag/ package (hmm, qqs, fxy, utils, main) - Convert imports to relative - Add pyproject.toml with hatchling build, console script entry point - Add phlag/__init__.py with version 0.0.2 - Clean discretizer.py dead code, vectorize DTO, rename to fxy.py - Fix concentration typos in hmm.py, add explicit imports - Implement validate_parameters in main.py - Add test/check_output.py for output validation - Update README: pip install, phlag command, expanded docs - Remove sbag.py
1 parent 87405f8 commit 5d70724

15 files changed

Lines changed: 378 additions & 1299 deletions

README.md

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,66 +4,91 @@
44

55
Given a species tree and a sequence of gene trees, Phlag aims to detect strong deviations from the multi-species coalescent (MSC) process using quadripartition quartet scores (QQS) and a hidden Markov model (HMM).
66

7-
The input is a sequence of gene trees, ideally sampled uniformly across a chromosome. The expected output is a set of "flagged" subsequences corresponding to model violations (e.g., admixture, recombination suppression, or dramatic changes in effective population size) and an updated species tree with new branch length (in coalescent units), estimated by ignoring the flagged gene trees.
7+
The input is a sequence of gene trees, ideally sampled uniformly across a chromosome.
8+
The expected output is a set of "flagged" subsequences corresponding to model violations (e.g., introgression, recombination suppression, or dramatic changes in effective population size) and an updated species tree with branch lengths in coalescent units (CU), estimated by ignoring the flagged gene trees.
89

910
## Installation
1011

11-
You will need a Python 3.9 environment. To install, follow the steps below:
12-
12+
Phlag requires Python == 3.9.
1313
```shell
1414
micromamba create -n phlag python=3.9 -y
1515
micromamba activate phlag
1616
git clone https://github.com/bo1929/phlag.git
1717
cd phlag
18-
pip install -r requirements.txt
19-
./build.sh
18+
pip install .
2019
```
2120

21+
<!-- You can simply use pip. -->
22+
<!-- ```shell -->
23+
<!-- pip install phlag -->
24+
<!-- ``` -->
25+
26+
<!-- Alternatively, install Phlag from the source. -->
27+
<!-- ```shell -->
28+
<!-- git clone https://github.com/bo1929/phlag.git -->
29+
<!-- cd phlag -->
30+
<!-- pip install . -->
31+
<!-- ``` -->
32+
2233
## Quickstart with a toy example
2334

24-
Suppose we have a species tree (`test/neoaves.nwk`) and an ordered set of gene trees (`test/emission.gtrees`).
25-
This gene tree sequence is a mixture of two coalescent with recombination processes, with one key difference: the effective population size for certain *Charadriiformes* increased 10-fold.
35+
The `test/` directory contains a simulated dataset based on a Neoaves species tree with 191 taxa.
36+
We use this to demonstrate how Phlag detects a genomic region where the MSC model is violated.
2637

27-
The mixture happens in a way that only a specific loci are affected by this change.
28-
In this particular example, 150 out of 1500 consecutive gene trees are generated by the alternative process.
29-
Our goal is to detect this region (gene trees in indices [913, 1062]) using Phlag.
38+
Suppose, we have a species tree (`test/neoaves.nwk`) and a sequence of 1500 gene trees (`test/emission.gtrees`), ordered along a chromosome.
39+
This gene tree sequence is a mixture of two coalescent with recombination (CwR) processes:
40+
- **Background (default)**: Gene trees simulated under the standard MSC with the original species tree parameters.
41+
- **Anomalous**: Gene trees simulated with a 10-fold increase in the effective population size for the branch leading to the *Charadriiformes* clade (labeled `N159`).
3042

43+
Out of 1500 consecutive gene trees, 150 (10%) come from the anomalous process.
44+
These occupy a single contiguous block at indices [913, 1062] (0-indexed, inclusive).
45+
Our goal is to detect this region using Phlag.
3146

3247
### Input
33-
* **Species tree**: A Newick species tree with labeled internal nodes.
34-
* **Gene trees**: One Newick tree per line, ordered along the genome (not necessarily with labeled internal nodes).
35-
* **Focal branch(es)**: The label of the clade(s) to be targeted. In this example, the clade under suspicion is labeled `N159`.
48+
* **Species tree** (`-s`): A Newick species tree with labeled internal nodes.
49+
* **Gene trees** (`-g`): One Newick tree per line, ordered along the genome (not necessarily with labeled internal nodes).
50+
* **Focal edge(s)** (`-e`): The label of the internal node(s) defining the edge(s) to target. In this example, the clade under suspicion is labeled `N159`.
3651

3752
### Usage
3853
```shell
39-
python phlag.py \
40-
--species-tree test/neoaves.nwk \
41-
--gene-trees test/emission.gtrees \
42-
-c N159 \
43-
--num-iters 10 \
44-
--read-qqs-freqs test/qqs.tsv \
45-
--output-file results-neoaves-N159.txt
54+
phlag \
55+
-s test/neoaves.nwk \
56+
-g test/emission.gtrees \
57+
-e N159 \
58+
-L 10 \
59+
--read-qqs-path test/qqs.tsv \
60+
-o results-neoaves-N159.txt
4661
```
47-
The quadripartition quartet scores are already computed and stored in `test/qqs.tsv`.
48-
You can directly read those to avoid extra computation for this toy example.
49-
If you do not give `--read-qqs-freqs`, QQS values need to be computed, which adds to the running time.
50-
Note that you can also save QQS values to read later for different runs (i.e., changing the focal branch or the hyperparameters) using `write-qqs-freqs`.
5162

52-
For other options, run `python phlag.py --help`.
63+
The quadripartition quartet scores for this dataset are precomputed and stored in `test/qqs.tsv`.
64+
Reading them with `--read-qqs-path` avoids recomputing QQS values from scratch.
65+
If you omit this flag, QQS values will be computed on the fly, which adds to the running time.
66+
You can also precompute and save QQS values for later runs (e.g., with different focal edges or hyperparameters) using `--write-qqs-path`.
67+
68+
For the full list of options, run `phlag --help`.
69+
70+
### Key hyperparameters
71+
72+
| Flag | Default | Description |
73+
|------|---------|-------------|
74+
| `--rho` | 0.9 | Controls sensitivity; reduce to flag more regions |
75+
| `--beta` | 5 | Controls contiguity of flagged intervals; reduce to merge nearby flags |
76+
| `--emission-lambda` | 1.0 | Controls expected deviation of anomalies from MSC |
77+
| `--eta` | 0.5 | Occupancy bias penalty on the marginal log-likelihood |
5378

5479
**Note**: If your data lacks strong deviations from the MSC, you may see few or no flagged trees.
55-
Conversely, an incorrect/unreliable species tree may result in too many flagged gene trees.
56-
In such cases, you can attempt to adjust the hyperparameters to detect anomalies at the resolution you desire.
80+
Conversely, an incorrect or unreliable species tree may result in too many flagged gene trees.
81+
In such cases, you can adjust the hyperparameters to detect anomalies at the resolution you desire.
5782

5883
A high-level and generic suggestion is running Phlag with varying hyperparameters (in a sensible range tested in the paper), and focusing on regions that are consistently detected. You may want to increase `--expected-num-anomalies` if you are looking for more granular results or if large contigous chunks are flagged. Similarly, `--expected-anamoly-proportion` can be varied between 0 and 0.5 to change to prior on the stationary distribution.
5984

6085
### Output
61-
The output is rather simple to interpret:
62-
- **Header (Lines 1-5)**: Auxiliary information prefixed with `#`:
63-
* Simply the invoked command and the version information.
86+
The output file contains:
87+
- **Header lines** (prefixed with `#`):
88+
* The invoked command.
6489
* The initial species tree with branch lengths in CU, estimated using the entire gene tree sequence.
65-
* The same species tree topology, but branch lengths are estimated only based on the gene trees that are not flagged.
66-
* The labels of the clades for the focal branches that are used, and their final branch lengths.
67-
* Distances between the emission distributions of the two states, separately for all the focal branches following the order given in the above line.
68-
- **State label**: `1` corresponds to flagged gene trees; `0` corresponds to MSC-compliant gene trees.
69-
- **Posterior probabilities**: The decoding probability for each gene tree.
90+
* The labels and final branch lengths of the focal edges.
91+
* The final species tree with branch lengths re-estimated after excluding the flagged gene trees.
92+
* The final focal edge lengths, and distances between the emission distributions of the two HMM states for each focal edge.
93+
- **State labels**: A comma-separated sequence where `1` = flagged (anomalous) and `0` = MSC-compliant. Gene trees excluded due to missing data are labeled `nan`.
94+
- **Posterior probabilities**: The smoothed posterior probability of being in the anomalous state for each gene tree.

TODO.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# TODO
2+
3+
* Make sure that both labels around the root gives you the same QQS values.
4+
5+
## Code Issues
6+
7+
### Design Improvements
8+
9+
- **`phlag.py` (heavy `__init__`)**: Constructing a `Phlag` object triggers tree parsing, QQS computation, edge length estimation, discretization, HMM initialization, and output header writing. This makes it impossible to test or use any step in isolation. A more conventional pattern would be a lightweight `__init__` followed by explicit method calls.
10+
- **`hmm.py`**: Wildcard import `from dynamax.hidden_markov_model.inference import *` still present. Key symbols (`partial`, `jit`, `lax`, `Callable`) now have explicit imports, but `HMMPosterior`, `HMMPosteriorFiltered`, `get_trans_mat`, `compute_transition_probs` still come from the wildcard.
11+
- **`qqs.py` (performance)**: `MSC.compute_qqs` is O(n_edges * n_gt * n_taxa) in pure Python. The inner loop iterates every gene tree for every edge, calling `QQS.compute()` which traverses the tree. This was previously Cython. For large datasets this will be a major bottleneck. Consider Cython/Numba/JAX acceleration for the inner loop.

build.sh

Lines changed: 0 additions & 33 deletions
This file was deleted.

0 commit comments

Comments
 (0)