PythiaLabelGenerator 1.1.1__tar.gz → 1.2.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.
- pythialabelgenerator-1.2.0/.gitattributes +1 -0
- pythialabelgenerator-1.2.0/.github/workflows/publish-pypi.yml +63 -0
- pythialabelgenerator-1.2.0/.github/workflows/test-label-generator.yml +89 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/.gitignore +3 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/.pre-commit-config.yaml +2 -2
- pythialabelgenerator-1.1.1/README.md → pythialabelgenerator-1.2.0/PKG-INFO +70 -6
- pythialabelgenerator-1.1.1/PKG-INFO → pythialabelgenerator-1.2.0/README.md +50 -26
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/labelgenerator/iqtree.py +1 -2
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/labelgenerator/label.py +6 -7
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/labelgenerator/logger.py +2 -1
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/labelgenerator/main.py +6 -6
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/labelgenerator/raxmlng.py +9 -3
- pythialabelgenerator-1.2.0/pixi.toml +55 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/pyproject.toml +12 -31
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/conftest.py +14 -3
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/test_label.py +25 -12
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/test_main.py +21 -10
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/test_raxmlng.py +38 -4
- pythialabelgenerator-1.1.1/.github/actions/setup-iqtree/action.yml +0 -22
- pythialabelgenerator-1.1.1/.github/actions/setup-raxmlng/action.yml +0 -20
- pythialabelgenerator-1.1.1/.github/actions/test-label-cli/action.yml +0 -12
- pythialabelgenerator-1.1.1/.github/workflows/test-label-generator.yml +0 -72
- pythialabelgenerator-1.1.1/etc/environment.yml +0 -9
- pythialabelgenerator-1.1.1/tests/test_config.py +0 -2
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/LICENSE +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/__init__.py +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/examples/example.phy +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/labelgenerator/__init__.py +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/labelgenerator/iqtree_parser.py +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/setup.py +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/__init__.py +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/AA.phy +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/DNA.phy +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/MORPH.phy +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/AA.iqtree.iqtree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/AA.iqtree.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/DNA.iqtree.iqtree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/DNA.iqtree.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/DNA.raxml.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/MORPH.iqtree.iqtree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/MORPH.iqtree.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.bestModel +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.bestTree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.mlTrees +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.rba +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.startTree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.rfdist.raxml.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.rfdist.raxml.rfDistances +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.bestModel +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.bestTree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.mlTrees +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.rba +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.startTree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.bestModel +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.bestTree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.bestTreeCollapsed +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.mlTrees +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.rba +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.startTree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.bestModel +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.bestTree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.bestTreeCollapsed +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.log +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.mlTrees +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.rba +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.startTree +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/test_iqtree.py +0 -0
- {pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/test_iqtree_parser.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pixi.lock linguist-generated=true
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: pypi-${{ github.event.release.tag_name }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
validate-release:
|
|
16
|
+
name: Validate release version
|
|
17
|
+
if: github.event_name == 'release' && github.event.action == 'published' && !github.event.release.draft && !github.event.release.prerelease
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- name: Check out released commit
|
|
21
|
+
uses: actions/checkout@v7
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v7
|
|
24
|
+
with:
|
|
25
|
+
python-version: '3.14'
|
|
26
|
+
- name: Check tag matches package version
|
|
27
|
+
env:
|
|
28
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
29
|
+
run: |
|
|
30
|
+
python - <<'PY'
|
|
31
|
+
import os
|
|
32
|
+
import pathlib
|
|
33
|
+
import tomllib
|
|
34
|
+
|
|
35
|
+
version = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]
|
|
36
|
+
tag = os.environ["RELEASE_TAG"]
|
|
37
|
+
if tag != version:
|
|
38
|
+
raise SystemExit(f"Release tag {tag!r} must match package version {version!r}")
|
|
39
|
+
print(f"Validated release {version}")
|
|
40
|
+
PY
|
|
41
|
+
|
|
42
|
+
test-and-build:
|
|
43
|
+
name: Test and build release
|
|
44
|
+
needs: validate-release
|
|
45
|
+
uses: ./.github/workflows/test-label-generator.yml
|
|
46
|
+
|
|
47
|
+
publish:
|
|
48
|
+
name: Upload distributions to PyPI
|
|
49
|
+
needs: test-and-build
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
environment:
|
|
52
|
+
name: pypi
|
|
53
|
+
url: https://pypi.org/project/PythiaLabelGenerator/
|
|
54
|
+
permissions:
|
|
55
|
+
id-token: write
|
|
56
|
+
steps:
|
|
57
|
+
- name: Download tested distributions
|
|
58
|
+
uses: actions/download-artifact@v8
|
|
59
|
+
with:
|
|
60
|
+
name: python-distributions
|
|
61
|
+
path: dist/
|
|
62
|
+
- name: Publish distributions
|
|
63
|
+
uses: pypa/gh-action-pypi-publish@v1.14.2
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
name: Build and test LabelGenerator
|
|
2
|
+
run-name: Build and test the LabelGenerator Python library
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
workflow_call:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
pixi-tests:
|
|
17
|
+
name: Pixi tests (${{ matrix.os }}, ${{ matrix.environment }})
|
|
18
|
+
runs-on: ${{ matrix.os }}
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
os: [ubuntu-latest, macos-latest]
|
|
23
|
+
environment: [py311, py314, legacy-raxml]
|
|
24
|
+
steps:
|
|
25
|
+
- name: Check out repository code
|
|
26
|
+
uses: actions/checkout@v7
|
|
27
|
+
- name: Set up Pixi
|
|
28
|
+
uses: prefix-dev/setup-pixi@v0.10.2
|
|
29
|
+
with:
|
|
30
|
+
pixi-version: v0.77.0
|
|
31
|
+
environments: ${{ matrix.environment }}
|
|
32
|
+
frozen: true
|
|
33
|
+
cache: true
|
|
34
|
+
cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
|
|
35
|
+
- name: Check the lock file
|
|
36
|
+
run: pixi lock --check
|
|
37
|
+
- name: Check RAxML-NG and IQ-TREE
|
|
38
|
+
run: pixi run --frozen --environment ${{ matrix.environment }} binary-smoke
|
|
39
|
+
- name: Run the test suite
|
|
40
|
+
run: pixi run --frozen --environment ${{ matrix.environment }} test
|
|
41
|
+
- name: Check the CLI
|
|
42
|
+
run: pixi run --frozen --environment ${{ matrix.environment }} cli-smoke
|
|
43
|
+
|
|
44
|
+
package-smoke:
|
|
45
|
+
name: Python package smoke test (${{ matrix.os }})
|
|
46
|
+
runs-on: ${{ matrix.os }}
|
|
47
|
+
strategy:
|
|
48
|
+
fail-fast: false
|
|
49
|
+
matrix:
|
|
50
|
+
os: [ubuntu-latest, macos-latest]
|
|
51
|
+
steps:
|
|
52
|
+
- name: Check out repository code
|
|
53
|
+
uses: actions/checkout@v7
|
|
54
|
+
- name: Set up Pixi
|
|
55
|
+
uses: prefix-dev/setup-pixi@v0.10.2
|
|
56
|
+
with:
|
|
57
|
+
pixi-version: v0.77.0
|
|
58
|
+
environments: build
|
|
59
|
+
frozen: true
|
|
60
|
+
cache: true
|
|
61
|
+
- name: Check the lock file
|
|
62
|
+
run: pixi lock --check
|
|
63
|
+
- name: Build and verify distributions
|
|
64
|
+
run: pixi run --frozen package-smoke
|
|
65
|
+
- name: Check distribution filenames
|
|
66
|
+
run: |
|
|
67
|
+
pixi run --frozen --environment build python - <<'PY'
|
|
68
|
+
import pathlib
|
|
69
|
+
import tomllib
|
|
70
|
+
|
|
71
|
+
version = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]
|
|
72
|
+
expected = {
|
|
73
|
+
f"pythialabelgenerator-{version}-py3-none-any.whl",
|
|
74
|
+
f"pythialabelgenerator-{version}.tar.gz",
|
|
75
|
+
}
|
|
76
|
+
actual = {path.name for path in pathlib.Path("dist").iterdir()}
|
|
77
|
+
if actual != expected:
|
|
78
|
+
raise SystemExit(f"Expected distributions {sorted(expected)}, found {sorted(actual)}")
|
|
79
|
+
PY
|
|
80
|
+
- name: Upload tested distributions
|
|
81
|
+
if: matrix.os == 'ubuntu-latest'
|
|
82
|
+
uses: actions/upload-artifact@v7
|
|
83
|
+
with:
|
|
84
|
+
name: python-distributions
|
|
85
|
+
path: |
|
|
86
|
+
dist/*.whl
|
|
87
|
+
dist/*.tar.gz
|
|
88
|
+
if-no-files-found: error
|
|
89
|
+
overwrite: true
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
-
rev:
|
|
3
|
+
rev: v6.0.0
|
|
4
4
|
hooks:
|
|
5
5
|
- id: check-yaml
|
|
6
6
|
- id: end-of-file-fixer
|
|
7
7
|
- id: trailing-whitespace
|
|
8
8
|
- id: detect-private-key
|
|
9
9
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
10
|
-
rev: v0.
|
|
10
|
+
rev: v0.16.6
|
|
11
11
|
hooks:
|
|
12
12
|
# Run the linter.
|
|
13
13
|
- id: ruff
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: PythiaLabelGenerator
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Command line tool to generate the ground-truth phylogenetic difficulty of MSAs
|
|
5
|
+
Project-URL: Homepage, https://github.com/tschuelia/PythiaLabelGenerator
|
|
6
|
+
Author-email: Julia Haag <info@juliaschmid.com>
|
|
7
|
+
License-Expression: GPL-3.0-or-later
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Python: <3.15,>=3.11
|
|
14
|
+
Requires-Dist: loguru
|
|
15
|
+
Requires-Dist: pythiaphylopredictor>=2.1.0
|
|
16
|
+
Requires-Dist: regex
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: pytest; extra == 'test'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
1
21
|
# Pythia Difficulty Label Generator (PyDLG)
|
|
2
22
|
|
|
3
23
|

|
|
@@ -39,6 +59,18 @@ Per default, the difficulty is based on $`N_{\text{all}}=100`$ ML trees.
|
|
|
39
59
|
Note that this number can be adjusted by the user, however, the difficulty will only be an approximation if the number
|
|
40
60
|
of trees is changed.
|
|
41
61
|
|
|
62
|
+
## RAxML-NG 2 Compatibility
|
|
63
|
+
|
|
64
|
+
When using RAxML-NG 2, PyDLG automatically passes the following flags to preserve the label-generation procedure
|
|
65
|
+
used in our publications:
|
|
66
|
+
|
|
67
|
+
- `--adaptive off` disables adaptive search and its early-stopping rule. This ensures that tree searches are not
|
|
68
|
+
cut short based on estimated difficulty and that label computation follows our published difficulty definition.
|
|
69
|
+
- `--extra brlen-start-fixed` restores the initial branch-length behavior of RAxML-NG 1.2, so labels are computed
|
|
70
|
+
using the same initialization as in our publications.
|
|
71
|
+
|
|
72
|
+
These flags are applied automatically; no additional `label` arguments are required.
|
|
73
|
+
|
|
42
74
|
## Prediction of Phylogenetic Difficulty
|
|
43
75
|
|
|
44
76
|
As stated above, computing the ground-truth difficulty for an MSA is very time-consuming and requires a lot of
|
|
@@ -62,7 +94,7 @@ biological morphological data, Pythia should work just fine 😉
|
|
|
62
94
|
To use PyDLG, you need to install
|
|
63
95
|
|
|
64
96
|
- RAxML-NG: See [the RAxML-NG GitHub repository](https://github.com/amkozlov/raxml-ng) for installation instructions.
|
|
65
|
-
|
|
97
|
+
PyDLG supports both RAxML-NG 1.x and 2.x.
|
|
66
98
|
- IQ-TREE: See [the IQ-TREE website](http://www.iqtree.org) for installation instructions. Please install IQ-TREE
|
|
67
99
|
version 2 or higher.
|
|
68
100
|
|
|
@@ -82,6 +114,38 @@ You can install the package using pip:
|
|
|
82
114
|
pip install pythialabelgenerator
|
|
83
115
|
```
|
|
84
116
|
|
|
117
|
+
This installs the Python package but not RAxML-NG or IQ-TREE. Install both programs separately as described in
|
|
118
|
+
[Requirements](#requirements).
|
|
119
|
+
|
|
120
|
+
#### Develop using Pixi
|
|
121
|
+
|
|
122
|
+
Pixi is the supported way to create a development environment containing the Python package and both phylogenetic
|
|
123
|
+
binaries:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
git clone https://github.com/tschuelia/PythiaLabelGenerator.git
|
|
127
|
+
cd PythiaLabelGenerator
|
|
128
|
+
pixi install --frozen
|
|
129
|
+
pixi run binary-smoke
|
|
130
|
+
pixi run cli-smoke
|
|
131
|
+
pixi run test
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Unlike released-package installations, these project-local Pixi environments install the tested binaries from
|
|
135
|
+
Bioconda. The default environment contains RAxML-NG 2.0.2 and IQ-TREE 2.4.0, while `legacy-raxml` contains RAxML-NG
|
|
136
|
+
1.2.2. PyPythia 2.1.0 or newer is installed from conda-forge.
|
|
137
|
+
|
|
138
|
+
The default environment uses Python 3.14 and RAxML-NG 2.0.2. Use `pixi run --environment py311 ...` or
|
|
139
|
+
`pixi run --environment py314 ...` to reproduce the minimum and maximum Python versions tested in CI, or
|
|
140
|
+
`pixi run --environment legacy-raxml ...` to test with RAxML-NG 1.2.2. PyDLG supports Python 3.11 through Python 3.14.
|
|
141
|
+
|
|
142
|
+
Install the pre-commit hooks once and run all checks on demand using the dedicated environment:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pixi run --environment pre-commit pre-commit-install
|
|
146
|
+
pixi run --environment pre-commit pre-commit-run
|
|
147
|
+
```
|
|
148
|
+
|
|
85
149
|
## Usage
|
|
86
150
|
|
|
87
151
|
PyDLG is primarily a command line tool. You can call it using the `label` command, for instance, to
|
|
@@ -96,9 +160,9 @@ to the console.
|
|
|
96
160
|
The output will look something like this:
|
|
97
161
|
|
|
98
162
|
```text
|
|
99
|
-
PyDLG version 1.0
|
|
163
|
+
PyDLG version 1.2.0 released by The Exelixis Lab
|
|
100
164
|
Developed by: Julia Haag
|
|
101
|
-
Latest version: https://github.com/tschuelia/
|
|
165
|
+
Latest version: https://github.com/tschuelia/PythiaLabelGenerator
|
|
102
166
|
Questions/problems/suggestions? Please open an issue on GitHub.
|
|
103
167
|
|
|
104
168
|
LabelGenerator was called at 06-Mar-2025 15:15:03 as follows:
|
|
@@ -126,14 +190,14 @@ Depending on your system setup, you might need to pass a RAxML-NG and IQ-TREE bi
|
|
|
126
190
|
You can do this using the `-r` and `-i` options, respectively. This is required in case `raxml-ng` and/or `iqtree2` are
|
|
127
191
|
not in your `$PATH`.
|
|
128
192
|
|
|
129
|
-
Note that this `
|
|
193
|
+
Note that this `example.phy` MSA is not the same exemplary MSA as we provide in the PyPythia repository, so please don't compare this ground-truth label to the exemplary prediction in PyPythia 😉
|
|
130
194
|
|
|
131
195
|
For a full list of command line options, run `label -h`:
|
|
132
196
|
|
|
133
197
|
```text
|
|
134
|
-
PyDLG version 1.0
|
|
198
|
+
PyDLG version 1.2.0 released by The Exelixis Lab
|
|
135
199
|
Developed by: Julia Haag
|
|
136
|
-
Latest version: https://github.com/tschuelia/
|
|
200
|
+
Latest version: https://github.com/tschuelia/PythiaLabelGenerator
|
|
137
201
|
Questions/problems/suggestions? Please open an issue on GitHub.
|
|
138
202
|
|
|
139
203
|
usage: label [-h] -m MSA -r RAXMLNG -i IQTREE [-t THREADS] [-s SEED] [-p PREFIX] [--model MODEL] [--ntrees NTREES] [--redo] [-V]
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: PythiaLabelGenerator
|
|
3
|
-
Version: 1.1.1
|
|
4
|
-
Summary: Command line tool to generate the ground-truth phylogenetic difficulty of MSAs
|
|
5
|
-
Project-URL: Homepage, https://github.com/tschuelia/PythiaLabelGenerator
|
|
6
|
-
Author-email: Julia Haag <info@juliaschmid.com>
|
|
7
|
-
License-Expression: GPL-3.0-or-later
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Requires-Python: <3.13,>=3.9
|
|
14
|
-
Requires-Dist: loguru
|
|
15
|
-
Requires-Dist: pythiaphylopredictor>=2.0.0
|
|
16
|
-
Requires-Dist: regex
|
|
17
|
-
Provides-Extra: test
|
|
18
|
-
Requires-Dist: pytest; extra == 'test'
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
|
|
21
1
|
# Pythia Difficulty Label Generator (PyDLG)
|
|
22
2
|
|
|
23
3
|

|
|
@@ -59,6 +39,18 @@ Per default, the difficulty is based on $`N_{\text{all}}=100`$ ML trees.
|
|
|
59
39
|
Note that this number can be adjusted by the user, however, the difficulty will only be an approximation if the number
|
|
60
40
|
of trees is changed.
|
|
61
41
|
|
|
42
|
+
## RAxML-NG 2 Compatibility
|
|
43
|
+
|
|
44
|
+
When using RAxML-NG 2, PyDLG automatically passes the following flags to preserve the label-generation procedure
|
|
45
|
+
used in our publications:
|
|
46
|
+
|
|
47
|
+
- `--adaptive off` disables adaptive search and its early-stopping rule. This ensures that tree searches are not
|
|
48
|
+
cut short based on estimated difficulty and that label computation follows our published difficulty definition.
|
|
49
|
+
- `--extra brlen-start-fixed` restores the initial branch-length behavior of RAxML-NG 1.2, so labels are computed
|
|
50
|
+
using the same initialization as in our publications.
|
|
51
|
+
|
|
52
|
+
These flags are applied automatically; no additional `label` arguments are required.
|
|
53
|
+
|
|
62
54
|
## Prediction of Phylogenetic Difficulty
|
|
63
55
|
|
|
64
56
|
As stated above, computing the ground-truth difficulty for an MSA is very time-consuming and requires a lot of
|
|
@@ -82,7 +74,7 @@ biological morphological data, Pythia should work just fine 😉
|
|
|
82
74
|
To use PyDLG, you need to install
|
|
83
75
|
|
|
84
76
|
- RAxML-NG: See [the RAxML-NG GitHub repository](https://github.com/amkozlov/raxml-ng) for installation instructions.
|
|
85
|
-
|
|
77
|
+
PyDLG supports both RAxML-NG 1.x and 2.x.
|
|
86
78
|
- IQ-TREE: See [the IQ-TREE website](http://www.iqtree.org) for installation instructions. Please install IQ-TREE
|
|
87
79
|
version 2 or higher.
|
|
88
80
|
|
|
@@ -102,6 +94,38 @@ You can install the package using pip:
|
|
|
102
94
|
pip install pythialabelgenerator
|
|
103
95
|
```
|
|
104
96
|
|
|
97
|
+
This installs the Python package but not RAxML-NG or IQ-TREE. Install both programs separately as described in
|
|
98
|
+
[Requirements](#requirements).
|
|
99
|
+
|
|
100
|
+
#### Develop using Pixi
|
|
101
|
+
|
|
102
|
+
Pixi is the supported way to create a development environment containing the Python package and both phylogenetic
|
|
103
|
+
binaries:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
git clone https://github.com/tschuelia/PythiaLabelGenerator.git
|
|
107
|
+
cd PythiaLabelGenerator
|
|
108
|
+
pixi install --frozen
|
|
109
|
+
pixi run binary-smoke
|
|
110
|
+
pixi run cli-smoke
|
|
111
|
+
pixi run test
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Unlike released-package installations, these project-local Pixi environments install the tested binaries from
|
|
115
|
+
Bioconda. The default environment contains RAxML-NG 2.0.2 and IQ-TREE 2.4.0, while `legacy-raxml` contains RAxML-NG
|
|
116
|
+
1.2.2. PyPythia 2.1.0 or newer is installed from conda-forge.
|
|
117
|
+
|
|
118
|
+
The default environment uses Python 3.14 and RAxML-NG 2.0.2. Use `pixi run --environment py311 ...` or
|
|
119
|
+
`pixi run --environment py314 ...` to reproduce the minimum and maximum Python versions tested in CI, or
|
|
120
|
+
`pixi run --environment legacy-raxml ...` to test with RAxML-NG 1.2.2. PyDLG supports Python 3.11 through Python 3.14.
|
|
121
|
+
|
|
122
|
+
Install the pre-commit hooks once and run all checks on demand using the dedicated environment:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pixi run --environment pre-commit pre-commit-install
|
|
126
|
+
pixi run --environment pre-commit pre-commit-run
|
|
127
|
+
```
|
|
128
|
+
|
|
105
129
|
## Usage
|
|
106
130
|
|
|
107
131
|
PyDLG is primarily a command line tool. You can call it using the `label` command, for instance, to
|
|
@@ -116,9 +140,9 @@ to the console.
|
|
|
116
140
|
The output will look something like this:
|
|
117
141
|
|
|
118
142
|
```text
|
|
119
|
-
PyDLG version 1.0
|
|
143
|
+
PyDLG version 1.2.0 released by The Exelixis Lab
|
|
120
144
|
Developed by: Julia Haag
|
|
121
|
-
Latest version: https://github.com/tschuelia/
|
|
145
|
+
Latest version: https://github.com/tschuelia/PythiaLabelGenerator
|
|
122
146
|
Questions/problems/suggestions? Please open an issue on GitHub.
|
|
123
147
|
|
|
124
148
|
LabelGenerator was called at 06-Mar-2025 15:15:03 as follows:
|
|
@@ -146,14 +170,14 @@ Depending on your system setup, you might need to pass a RAxML-NG and IQ-TREE bi
|
|
|
146
170
|
You can do this using the `-r` and `-i` options, respectively. This is required in case `raxml-ng` and/or `iqtree2` are
|
|
147
171
|
not in your `$PATH`.
|
|
148
172
|
|
|
149
|
-
Note that this `
|
|
173
|
+
Note that this `example.phy` MSA is not the same exemplary MSA as we provide in the PyPythia repository, so please don't compare this ground-truth label to the exemplary prediction in PyPythia 😉
|
|
150
174
|
|
|
151
175
|
For a full list of command line options, run `label -h`:
|
|
152
176
|
|
|
153
177
|
```text
|
|
154
|
-
PyDLG version 1.0
|
|
178
|
+
PyDLG version 1.2.0 released by The Exelixis Lab
|
|
155
179
|
Developed by: Julia Haag
|
|
156
|
-
Latest version: https://github.com/tschuelia/
|
|
180
|
+
Latest version: https://github.com/tschuelia/PythiaLabelGenerator
|
|
157
181
|
Questions/problems/suggestions? Please open an issue on GitHub.
|
|
158
182
|
|
|
159
183
|
usage: label [-h] -m MSA -r RAXMLNG -i IQTREE [-t THREADS] [-s SEED] [-p PREFIX] [--model MODEL] [--ntrees NTREES] [--redo] [-V]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import pathlib
|
|
2
2
|
import subprocess
|
|
3
|
-
from typing import Optional
|
|
4
3
|
|
|
5
4
|
from pypythia.custom_types import DataType
|
|
6
5
|
|
|
@@ -49,7 +48,7 @@ def run_statstests(
|
|
|
49
48
|
model: str,
|
|
50
49
|
prefix: pathlib.Path,
|
|
51
50
|
seed: int = 0,
|
|
52
|
-
threads:
|
|
51
|
+
threads: int | None = None,
|
|
53
52
|
is_morph: bool = False,
|
|
54
53
|
redo: bool = False,
|
|
55
54
|
) -> None:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import pathlib
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
|
-
from pypythia.msa import
|
|
3
|
+
from pypythia.msa import MSA, DataType
|
|
5
4
|
|
|
6
5
|
from labelgenerator.iqtree import (
|
|
7
6
|
filter_plausible_trees,
|
|
@@ -92,9 +91,9 @@ def get_label(
|
|
|
92
91
|
label = total / 5
|
|
93
92
|
|
|
94
93
|
eps = 1e-9
|
|
95
|
-
assert (
|
|
96
|
-
|
|
97
|
-
)
|
|
94
|
+
assert -eps <= label <= 1 + eps, (
|
|
95
|
+
f"Label {label} is not between 0 and 1. Check the input values."
|
|
96
|
+
)
|
|
98
97
|
|
|
99
98
|
return label
|
|
100
99
|
|
|
@@ -105,10 +104,10 @@ def compute_label(
|
|
|
105
104
|
raxmlng: pathlib.Path,
|
|
106
105
|
iqtree: pathlib.Path,
|
|
107
106
|
prefix: pathlib.Path,
|
|
108
|
-
model:
|
|
107
|
+
model: str | None = None,
|
|
109
108
|
n_trees: int = 100,
|
|
110
109
|
seed: int = 0,
|
|
111
|
-
threads:
|
|
110
|
+
threads: int | None = None,
|
|
112
111
|
redo: bool = False,
|
|
113
112
|
log_info: bool = True,
|
|
114
113
|
) -> float:
|
|
@@ -3,6 +3,7 @@ import textwrap
|
|
|
3
3
|
import time
|
|
4
4
|
|
|
5
5
|
import loguru
|
|
6
|
+
|
|
6
7
|
from labelgenerator import __version__
|
|
7
8
|
|
|
8
9
|
SCRIPT_START = time.perf_counter()
|
|
@@ -17,7 +18,7 @@ def get_header():
|
|
|
17
18
|
return textwrap.dedent(
|
|
18
19
|
f"PyDLG version {__version__} released by The Exelixis Lab\n"
|
|
19
20
|
f"Developed by: Julia Haag\n"
|
|
20
|
-
f"Latest version: https://github.com/tschuelia/
|
|
21
|
+
f"Latest version: https://github.com/tschuelia/PythiaLabelGenerator\n"
|
|
21
22
|
f"Questions/problems/suggestions? Please open an issue on GitHub.\n",
|
|
22
23
|
)
|
|
23
24
|
|
|
@@ -3,7 +3,10 @@ import pathlib
|
|
|
3
3
|
import shutil
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
from pypythia.msa import parse_msa
|
|
8
|
+
from pypythia.prediction import collect_features
|
|
9
|
+
from pypythia.raxmlng import RAxMLNG
|
|
7
10
|
|
|
8
11
|
from labelgenerator import __version__
|
|
9
12
|
from labelgenerator.label import compute_label
|
|
@@ -14,9 +17,6 @@ from labelgenerator.logger import (
|
|
|
14
17
|
log_runtime_information,
|
|
15
18
|
logger,
|
|
16
19
|
)
|
|
17
|
-
from pypythia.msa import parse_msa
|
|
18
|
-
from pypythia.prediction import collect_features
|
|
19
|
-
from pypythia.raxmlng import RAxMLNG
|
|
20
20
|
|
|
21
21
|
DEFAULT_RAXMLNG_EXE = (
|
|
22
22
|
pathlib.Path(shutil.which("raxml-ng")) if shutil.which("raxml-ng") else None
|
|
@@ -27,7 +27,7 @@ DEFAULT_IQTREE_EXE = (
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def _parse_cli(arg_list:
|
|
30
|
+
def _parse_cli(arg_list: list[str] | None = None):
|
|
31
31
|
parser = argparse.ArgumentParser(
|
|
32
32
|
description="Generate the ground truth difficulty for the given MSA."
|
|
33
33
|
)
|
|
@@ -118,7 +118,7 @@ def _parse_cli(arg_list: Optional[list[str]] = None):
|
|
|
118
118
|
return parser.parse_args(arg_list)
|
|
119
119
|
|
|
120
120
|
|
|
121
|
-
def main(arg_list:
|
|
121
|
+
def main(arg_list: list[str] | None = None):
|
|
122
122
|
logger.info(get_header())
|
|
123
123
|
args = _parse_cli(arg_list)
|
|
124
124
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import math
|
|
2
2
|
import pathlib
|
|
3
|
-
from typing import Optional
|
|
4
3
|
|
|
5
4
|
from pypythia.raxmlng import RAxMLNG, get_raxmlng_rfdist_results, run_raxmlng_command
|
|
6
5
|
|
|
@@ -38,7 +37,7 @@ def infer_ml_trees(
|
|
|
38
37
|
prefix: pathlib.Path,
|
|
39
38
|
n_trees: int = 100,
|
|
40
39
|
seed: int = 0,
|
|
41
|
-
threads:
|
|
40
|
+
threads: int | None = None,
|
|
42
41
|
redo: bool = False,
|
|
43
42
|
) -> None:
|
|
44
43
|
"""
|
|
@@ -74,6 +73,8 @@ def infer_ml_trees(
|
|
|
74
73
|
if not redo and _inference_results_exist_and_correct(prefix, n_trees):
|
|
75
74
|
return
|
|
76
75
|
|
|
76
|
+
raxmlng_major_version = RAxMLNG(raxmlng)._major_version
|
|
77
|
+
|
|
77
78
|
n_pars_trees = math.ceil(n_trees / 2)
|
|
78
79
|
n_rand_trees = n_trees - n_pars_trees
|
|
79
80
|
rand_string = f",rand{{{n_rand_trees}}}" if n_rand_trees > 0 else ""
|
|
@@ -95,6 +96,11 @@ def infer_ml_trees(
|
|
|
95
96
|
if threads is not None:
|
|
96
97
|
cmd.extend(["--threads", threads])
|
|
97
98
|
|
|
99
|
+
if raxmlng_major_version >= 2:
|
|
100
|
+
# Preserve the v1.2 search and fixed parsimony starting branch lengths.
|
|
101
|
+
# Disabling adaptive search alone leaves v2's new initialization enabled.
|
|
102
|
+
cmd.extend(["--adaptive", "off", "--extra", "brlen-start-fixed"])
|
|
103
|
+
|
|
98
104
|
if redo:
|
|
99
105
|
cmd.append("--redo")
|
|
100
106
|
|
|
@@ -130,7 +136,7 @@ def rf_distance(
|
|
|
130
136
|
ml_trees: pathlib.Path,
|
|
131
137
|
prefix: pathlib.Path,
|
|
132
138
|
raxmlng: pathlib.Path,
|
|
133
|
-
n_trees:
|
|
139
|
+
n_trees: int | None = None,
|
|
134
140
|
redo: bool = False,
|
|
135
141
|
) -> tuple[int, float]:
|
|
136
142
|
"""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
channels = ["conda-forge", "bioconda"]
|
|
3
|
+
platforms = ["linux-64", "osx-64", "osx-arm64"]
|
|
4
|
+
requires-pixi = ">=0.76.1"
|
|
5
|
+
|
|
6
|
+
[dependencies]
|
|
7
|
+
pythiaphylopredictor = ">=2.1.0"
|
|
8
|
+
regex = "*"
|
|
9
|
+
loguru = "*"
|
|
10
|
+
iqtree = "==2.4.0"
|
|
11
|
+
|
|
12
|
+
[pypi-dependencies]
|
|
13
|
+
PythiaLabelGenerator = { path = ".", editable = true }
|
|
14
|
+
|
|
15
|
+
[feature.py311.dependencies]
|
|
16
|
+
python = "3.11.*"
|
|
17
|
+
|
|
18
|
+
[feature.py314.dependencies]
|
|
19
|
+
python = "3.14.*"
|
|
20
|
+
|
|
21
|
+
[feature.test.dependencies]
|
|
22
|
+
pytest = "*"
|
|
23
|
+
|
|
24
|
+
[feature.raxml1.dependencies]
|
|
25
|
+
raxml-ng = "==1.2.2"
|
|
26
|
+
|
|
27
|
+
[feature.raxml2.dependencies]
|
|
28
|
+
raxml-ng = "==2.0.2"
|
|
29
|
+
|
|
30
|
+
[feature.pre-commit.dependencies]
|
|
31
|
+
pre-commit = "*"
|
|
32
|
+
|
|
33
|
+
[feature.pre-commit.tasks]
|
|
34
|
+
pre-commit-install = "pre-commit install"
|
|
35
|
+
pre-commit-run = "pre-commit run --all-files"
|
|
36
|
+
|
|
37
|
+
[feature.build.dependencies]
|
|
38
|
+
hatchling = "*"
|
|
39
|
+
pip = "*"
|
|
40
|
+
python-build = "*"
|
|
41
|
+
|
|
42
|
+
[environments]
|
|
43
|
+
default = { features = ["py314", "raxml2", "test"] }
|
|
44
|
+
py311 = { features = ["py311", "raxml2", "test"] }
|
|
45
|
+
py314 = { features = ["py314", "raxml2", "test"] }
|
|
46
|
+
legacy-raxml = { features = ["py311", "raxml1", "test"] }
|
|
47
|
+
build = { features = ["py314", "build"] }
|
|
48
|
+
pre-commit = { features = ["pre-commit"], no-default-feature = true }
|
|
49
|
+
|
|
50
|
+
[tasks]
|
|
51
|
+
test = "pytest -svx --color=yes"
|
|
52
|
+
binary-smoke = "raxml-ng -v && iqtree2 -v"
|
|
53
|
+
cli-smoke = "label --help"
|
|
54
|
+
build = { cmd = "python -m build", default-environment = "build" }
|
|
55
|
+
package-smoke = { cmd = "python -m pip install --force-reinstall --no-deps dist/*.whl && python -m pip check && python -c 'import labelgenerator' && label --help", depends-on = ["build"], default-environment = "build" }
|
|
@@ -3,18 +3,18 @@ name = "PythiaLabelGenerator"
|
|
|
3
3
|
description = "Command line tool to generate the ground-truth phylogenetic difficulty of MSAs"
|
|
4
4
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
5
5
|
authors = [{name = "Julia Haag", email = "info@juliaschmid.com"}]
|
|
6
|
-
version = "1.
|
|
6
|
+
version = "1.2.0"
|
|
7
7
|
license = "GPL-3.0-or-later"
|
|
8
8
|
classifiers = [
|
|
9
|
-
"Programming Language :: Python :: 3.9",
|
|
10
|
-
"Programming Language :: Python :: 3.10",
|
|
11
9
|
"Programming Language :: Python :: 3.11",
|
|
12
|
-
"Programming Language :: Python :: 3.12"
|
|
10
|
+
"Programming Language :: Python :: 3.12",
|
|
11
|
+
"Programming Language :: Python :: 3.13",
|
|
12
|
+
"Programming Language :: Python :: 3.14"
|
|
13
13
|
]
|
|
14
|
-
requires-python = ">=
|
|
14
|
+
requires-python = ">=3.11,<3.15"
|
|
15
15
|
|
|
16
16
|
dependencies = [
|
|
17
|
-
"pythiaphylopredictor>=2.
|
|
17
|
+
"pythiaphylopredictor>=2.1.0",
|
|
18
18
|
"regex",
|
|
19
19
|
"loguru"
|
|
20
20
|
]
|
|
@@ -34,34 +34,15 @@ label = "labelgenerator.main:main"
|
|
|
34
34
|
requires = ["hatchling"]
|
|
35
35
|
build-backend = "hatchling.build"
|
|
36
36
|
|
|
37
|
-
[tool.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
\.eggs
|
|
41
|
-
| \.git
|
|
42
|
-
| \.venv
|
|
43
|
-
| build
|
|
44
|
-
| dist
|
|
45
|
-
)/
|
|
46
|
-
'''
|
|
47
|
-
|
|
48
|
-
[tool.isort]
|
|
49
|
-
multi_line_output = 3
|
|
50
|
-
include_trailing_comma = true
|
|
51
|
-
ensure_newline_before_comments = true
|
|
52
|
-
line_length = 88
|
|
53
|
-
known_first_party = "labelgenerator"
|
|
54
|
-
skip_glob = '\.eggs/*,\.git/*,\.venv/*,build/*,dist/*'
|
|
55
|
-
default_section = 'THIRDPARTY'
|
|
56
|
-
|
|
57
|
-
[tool.mypy]
|
|
58
|
-
python_version = 3.8
|
|
59
|
-
ignore_missing_imports = true
|
|
60
|
-
no_implicit_optional = true
|
|
61
|
-
check_untyped_defs = true
|
|
37
|
+
[tool.ruff.lint]
|
|
38
|
+
select = ["F", "I", "UP"]
|
|
39
|
+
ignore = ["E501"]
|
|
62
40
|
|
|
63
41
|
[tool.pytest.ini_options]
|
|
64
42
|
addopts = "--import-mode=importlib"
|
|
65
43
|
|
|
66
44
|
[tool.hatch.build.targets.wheel]
|
|
67
45
|
packages = ["labelgenerator"]
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.sdist]
|
|
48
|
+
exclude = ["/pixi.lock"]
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import pathlib
|
|
3
|
+
import shutil
|
|
2
4
|
|
|
3
5
|
import pytest
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
|
|
8
|
+
def _command_path(environment_variable, executable):
|
|
9
|
+
configured_command = os.environ.get(environment_variable)
|
|
10
|
+
command = configured_command or shutil.which(executable)
|
|
11
|
+
if command is None:
|
|
12
|
+
pytest.fail(
|
|
13
|
+
f"Could not find {executable!r}. Add it to PATH or set "
|
|
14
|
+
f"{environment_variable}."
|
|
15
|
+
)
|
|
16
|
+
return pathlib.Path(command)
|
|
6
17
|
|
|
7
18
|
|
|
8
19
|
@pytest.fixture
|
|
9
20
|
def raxmlng_command():
|
|
10
|
-
return
|
|
21
|
+
return _command_path("RAXMLNG_COMMAND", "raxml-ng")
|
|
11
22
|
|
|
12
23
|
|
|
13
24
|
@pytest.fixture
|
|
14
25
|
def iqtree_command():
|
|
15
|
-
return
|
|
26
|
+
return _command_path("IQTREE_COMMAND", "iqtree2")
|
|
16
27
|
|
|
17
28
|
|
|
18
29
|
@pytest.fixture
|
|
@@ -2,12 +2,12 @@ import pathlib
|
|
|
2
2
|
import tempfile
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
|
-
|
|
6
|
-
from labelgenerator.label import get_label, compute_label
|
|
7
|
-
from labelgenerator.logger import logger
|
|
8
5
|
from pypythia.custom_types import DataType
|
|
9
6
|
from pypythia.msa import parse_msa
|
|
10
7
|
|
|
8
|
+
from labelgenerator.label import compute_label, get_label
|
|
9
|
+
from labelgenerator.logger import logger
|
|
10
|
+
|
|
11
11
|
|
|
12
12
|
def test_get_label_fails_for_invalid_input():
|
|
13
13
|
with pytest.raises(
|
|
@@ -88,11 +88,21 @@ def test_get_label(values, expected):
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
@pytest.mark.parametrize(
|
|
91
|
-
"data_type, expected_label",
|
|
92
|
-
[
|
|
91
|
+
"data_type, expected_label, tolerance",
|
|
92
|
+
[
|
|
93
|
+
# Native RAxML-NG builds can select different near-optimal DNA trees.
|
|
94
|
+
(DataType.DNA, 0.772, 0.05),
|
|
95
|
+
(DataType.AA, 0.04, 0.01),
|
|
96
|
+
(DataType.MORPH, 0.173, 0.01),
|
|
97
|
+
],
|
|
93
98
|
)
|
|
94
99
|
def test_compute_label(
|
|
95
|
-
raxmlng_command,
|
|
100
|
+
raxmlng_command,
|
|
101
|
+
iqtree_command,
|
|
102
|
+
data_dir,
|
|
103
|
+
data_type,
|
|
104
|
+
expected_label,
|
|
105
|
+
tolerance,
|
|
96
106
|
):
|
|
97
107
|
msa_file = data_dir / f"{data_type.name}.phy"
|
|
98
108
|
|
|
@@ -109,7 +119,7 @@ def test_compute_label(
|
|
|
109
119
|
log_info=False,
|
|
110
120
|
seed=42,
|
|
111
121
|
)
|
|
112
|
-
assert label == pytest.approx(expected_label, abs=
|
|
122
|
+
assert label == pytest.approx(expected_label, abs=tolerance)
|
|
113
123
|
|
|
114
124
|
|
|
115
125
|
def test_compute_label_with_logging(raxmlng_command, iqtree_command, data_dir):
|
|
@@ -139,8 +149,11 @@ def test_compute_label_with_logging(raxmlng_command, iqtree_command, data_dir):
|
|
|
139
149
|
logfile_content = logfile.read_text()
|
|
140
150
|
|
|
141
151
|
assert f"Inferring {n_trees} ML trees using RAxML-NG." in logfile_content
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
expected_lines = [
|
|
153
|
+
"RF-Distance ML trees: 0.23",
|
|
154
|
+
"Unique topologies ML trees: 2",
|
|
155
|
+
"Found 10 plausible trees.",
|
|
156
|
+
"RF-Distance plausible trees: 0.23",
|
|
157
|
+
"Unique topologies plausible trees: 2",
|
|
158
|
+
]
|
|
159
|
+
assert all(line in logfile_content for line in expected_lines)
|
|
@@ -1,21 +1,32 @@
|
|
|
1
|
+
import pathlib
|
|
1
2
|
import tempfile
|
|
2
|
-
import pytest
|
|
3
3
|
|
|
4
4
|
import pandas as pd
|
|
5
|
-
|
|
6
|
-
from labelgenerator.main import main
|
|
7
|
-
from labelgenerator import __version__
|
|
8
|
-
import pathlib
|
|
9
|
-
|
|
5
|
+
import pytest
|
|
10
6
|
from pypythia.custom_types import DataType
|
|
11
7
|
from pypythia.msa import parse_msa
|
|
12
8
|
|
|
9
|
+
from labelgenerator import __version__
|
|
10
|
+
from labelgenerator.main import main
|
|
11
|
+
|
|
13
12
|
|
|
14
13
|
@pytest.mark.parametrize(
|
|
15
|
-
"data_type, expected_label",
|
|
16
|
-
[
|
|
14
|
+
"data_type, expected_label, tolerance",
|
|
15
|
+
[
|
|
16
|
+
# Native RAxML-NG builds can select different near-optimal DNA trees.
|
|
17
|
+
(DataType.DNA, 0.772, 0.05),
|
|
18
|
+
(DataType.AA, 0.04, 0.01),
|
|
19
|
+
(DataType.MORPH, 0.173, 0.01),
|
|
20
|
+
],
|
|
17
21
|
)
|
|
18
|
-
def test_main(
|
|
22
|
+
def test_main(
|
|
23
|
+
data_type,
|
|
24
|
+
expected_label,
|
|
25
|
+
tolerance,
|
|
26
|
+
data_dir,
|
|
27
|
+
raxmlng_command,
|
|
28
|
+
iqtree_command,
|
|
29
|
+
):
|
|
19
30
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
20
31
|
# Create a temporary directory for the test
|
|
21
32
|
prefix = pathlib.Path(tmpdir) / "test"
|
|
@@ -90,7 +101,7 @@ def test_main(data_type, expected_label, data_dir, raxmlng_command, iqtree_comma
|
|
|
90
101
|
|
|
91
102
|
# Check if the label is correct
|
|
92
103
|
label = features_content["difficulty"].values[0]
|
|
93
|
-
assert label == pytest.approx(expected_label, abs=
|
|
104
|
+
assert label == pytest.approx(expected_label, abs=tolerance)
|
|
94
105
|
|
|
95
106
|
# Check if the log file is correct and contains the expected output
|
|
96
107
|
expected_lines = [
|
|
@@ -8,6 +8,7 @@ import pytest
|
|
|
8
8
|
from pypythia.custom_types import DataType
|
|
9
9
|
from pypythia.msa import parse_msa
|
|
10
10
|
|
|
11
|
+
import labelgenerator.raxmlng as raxmlng_module
|
|
11
12
|
from labelgenerator.raxmlng import (
|
|
12
13
|
_inference_results_exist_and_correct,
|
|
13
14
|
_rfdist_results_exists_and_correct,
|
|
@@ -67,15 +68,48 @@ def test_infer_ml_trees(raxmlng_command, dna_msa, n_trees):
|
|
|
67
68
|
expected_random = (
|
|
68
69
|
f"random ({n_rand_expected}) + " if n_rand_expected > 0 else ""
|
|
69
70
|
)
|
|
70
|
-
|
|
71
|
-
f"parsimony ({n_pars_expected})" if n_pars_expected > 0 else ""
|
|
72
|
-
)
|
|
73
|
-
expected_log = f"start tree(s): {expected_random}{expected_parsimony}"
|
|
71
|
+
expected_log = f"start tree(s): {expected_random}parsimony ({n_pars_expected})"
|
|
74
72
|
|
|
75
73
|
log_file = prefix.with_suffix(".raxml.log")
|
|
76
74
|
assert expected_log in log_file.read_text()
|
|
77
75
|
|
|
78
76
|
|
|
77
|
+
@pytest.mark.parametrize(
|
|
78
|
+
("major_version", "expected_adaptive_setting"),
|
|
79
|
+
[(1, None), (2, "off"), (3, "off")],
|
|
80
|
+
)
|
|
81
|
+
def test_infer_ml_trees_preserves_v1_search_by_raxmlng_version(
|
|
82
|
+
tmp_path, monkeypatch, major_version, expected_adaptive_setting
|
|
83
|
+
):
|
|
84
|
+
class RAxMLNGStub:
|
|
85
|
+
def __init__(self, _executable):
|
|
86
|
+
self._major_version = major_version
|
|
87
|
+
|
|
88
|
+
commands = []
|
|
89
|
+
monkeypatch.setattr(raxmlng_module, "RAxMLNG", RAxMLNGStub)
|
|
90
|
+
monkeypatch.setattr(raxmlng_module, "run_raxmlng_command", commands.append)
|
|
91
|
+
|
|
92
|
+
raxmlng_module.infer_ml_trees(
|
|
93
|
+
msa=tmp_path / "msa.phy",
|
|
94
|
+
raxmlng=tmp_path / "raxml-ng",
|
|
95
|
+
model="GTR+G",
|
|
96
|
+
prefix=tmp_path / "inference",
|
|
97
|
+
n_trees=2,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
command = commands[0]
|
|
101
|
+
if expected_adaptive_setting is None:
|
|
102
|
+
assert "--adaptive" not in command
|
|
103
|
+
assert "--extra" not in command
|
|
104
|
+
else:
|
|
105
|
+
adaptive_index = command.index("--adaptive")
|
|
106
|
+
assert command[adaptive_index + 1] == expected_adaptive_setting
|
|
107
|
+
assert command.count("--adaptive") == 1
|
|
108
|
+
extra_index = command.index("--extra")
|
|
109
|
+
assert command[extra_index + 1] == "brlen-start-fixed"
|
|
110
|
+
assert command.count("--extra") == 1
|
|
111
|
+
|
|
112
|
+
|
|
79
113
|
@pytest.mark.parametrize("data_type", [DataType.DNA, DataType.AA, DataType.MORPH])
|
|
80
114
|
def test_infer_ml_trees_for_dtypes(raxmlng_command, data_dir, data_type):
|
|
81
115
|
msa = data_dir / f"{data_type.name}.phy"
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
name: Setup IQ-TREE
|
|
2
|
-
runs:
|
|
3
|
-
using: composite
|
|
4
|
-
steps:
|
|
5
|
-
- name: Download IQ-TREE MacOS
|
|
6
|
-
if: runner.os == 'macOS'
|
|
7
|
-
run: |
|
|
8
|
-
wget https://github.com/iqtree/iqtree2/releases/download/v2.4.0/iqtree-2.4.0-macOS.zip
|
|
9
|
-
unzip iqtree-*.zip && mv iqtree-*/* .
|
|
10
|
-
shell: bash
|
|
11
|
-
- name: Download IQ-TREE Linux
|
|
12
|
-
if: runner.os == 'Linux'
|
|
13
|
-
run: |
|
|
14
|
-
wget https://github.com/iqtree/iqtree2/releases/download/v2.4.0/iqtree-2.4.0-Linux-intel.tar.gz
|
|
15
|
-
tar xvf iqtree-*.tar.gz && mv iqtree-*/* .
|
|
16
|
-
shell: bash
|
|
17
|
-
- name: Unzip IQ-TREE and set Env variable
|
|
18
|
-
run: echo iqtree2=$(pwd)/bin/iqtree2 >> $GITHUB_ENV
|
|
19
|
-
shell: bash
|
|
20
|
-
- name: Check IQ-TREE installation
|
|
21
|
-
run: $iqtree2 -v
|
|
22
|
-
shell: bash
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
name: Setup RAxML-NG
|
|
2
|
-
runs:
|
|
3
|
-
using: composite
|
|
4
|
-
steps:
|
|
5
|
-
- name: Download RAxML-NG MacOS
|
|
6
|
-
if: runner.os == 'macOS'
|
|
7
|
-
run: wget https://github.com/amkozlov/raxml-ng/releases/download/1.2.2/raxml-ng_v1.2.2_macos.zip
|
|
8
|
-
shell: bash
|
|
9
|
-
- name: Download RAxML-NG Linux
|
|
10
|
-
if: runner.os == 'Linux'
|
|
11
|
-
run: wget https://github.com/amkozlov/raxml-ng/releases/download/1.2.2/raxml-ng_v1.2.2_linux_x86_64.zip
|
|
12
|
-
shell: bash
|
|
13
|
-
- name: Unzip RAxML-NG and set Env variable
|
|
14
|
-
run: |
|
|
15
|
-
unzip raxml-ng_*.zip
|
|
16
|
-
echo raxmlng=$(pwd)/raxml-ng >> $GITHUB_ENV
|
|
17
|
-
shell: bash
|
|
18
|
-
- name: Check RAxML-NG installation
|
|
19
|
-
run: $raxmlng -v
|
|
20
|
-
shell: bash
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
name: Test Label CLI
|
|
2
|
-
inputs:
|
|
3
|
-
msa-file:
|
|
4
|
-
description: MSA file to test Label CLI with
|
|
5
|
-
default: examples/example.phy
|
|
6
|
-
runs:
|
|
7
|
-
using: composite
|
|
8
|
-
steps:
|
|
9
|
-
- name: Run Label CLI with an exemplary MSA to make sure everything is working
|
|
10
|
-
run: |
|
|
11
|
-
label -m ${{ inputs.msa-file }} -r ${{ env.raxmlng }} -i ${{ env.iqtree2 }} --ntrees 4
|
|
12
|
-
shell: bash -el {0}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
name: Build and test LabelGenerator
|
|
2
|
-
run-name: Build and run the tests of the LabelGenerator python library.
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
paths-ignore:
|
|
6
|
-
- 'docs/**'
|
|
7
|
-
jobs:
|
|
8
|
-
Run-Label-Generator-Tests:
|
|
9
|
-
runs-on: ${{ matrix.os }}
|
|
10
|
-
strategy:
|
|
11
|
-
matrix:
|
|
12
|
-
os: [ubuntu-latest, macos-latest]
|
|
13
|
-
python-version: ["3.9", "3.12"]
|
|
14
|
-
steps:
|
|
15
|
-
- name: Check out repository code
|
|
16
|
-
uses: actions/checkout@v3
|
|
17
|
-
- name: Setup Conda
|
|
18
|
-
uses: mamba-org/setup-micromamba@v2
|
|
19
|
-
with:
|
|
20
|
-
environment-file: etc/environment.yml
|
|
21
|
-
cache-environment: true
|
|
22
|
-
create-args:
|
|
23
|
-
python=${{ matrix.python-version }}
|
|
24
|
-
generate-run-shell: false
|
|
25
|
-
post-cleanup: none
|
|
26
|
-
- name: Setup RAxML-NG and RAxML-NG env variable
|
|
27
|
-
uses: ./.github/actions/setup-raxmlng
|
|
28
|
-
- name: Setup IQ-TREE and IQ-TREE env variable
|
|
29
|
-
uses: ./.github/actions/setup-iqtree
|
|
30
|
-
- name: Install LabelGenerator and setup requirements
|
|
31
|
-
run: |
|
|
32
|
-
pip install -e . --no-deps
|
|
33
|
-
rm tests/test_config.py
|
|
34
|
-
echo "RAXMLNG_COMMAND = '${{ env.raxmlng }}'" >> tests/test_config.py
|
|
35
|
-
echo "IQTREE_COMMAND = '${{ env.iqtree2 }}'" >> tests/test_config.py
|
|
36
|
-
cat tests/test_config.py
|
|
37
|
-
shell: bash -el {0}
|
|
38
|
-
# - name: Setup tmate session
|
|
39
|
-
# uses: mxschmitt/action-tmate@v3
|
|
40
|
-
- name: Run LabelGenerator tests
|
|
41
|
-
run: |
|
|
42
|
-
PYTHONPATH=. pytest -svx --color=yes
|
|
43
|
-
shell: bash -el {0}
|
|
44
|
-
|
|
45
|
-
Install-using-conda:
|
|
46
|
-
runs-on: ${{ matrix.os }}
|
|
47
|
-
strategy:
|
|
48
|
-
matrix:
|
|
49
|
-
os: [ubuntu-latest, macos-latest]
|
|
50
|
-
python-version: ["3.9", "3.12"]
|
|
51
|
-
steps:
|
|
52
|
-
- name: Check out repository code
|
|
53
|
-
uses: actions/checkout@v3
|
|
54
|
-
- name: Setup Conda
|
|
55
|
-
uses: mamba-org/setup-micromamba@v2
|
|
56
|
-
with:
|
|
57
|
-
environment-file: etc/environment.yml
|
|
58
|
-
cache-environment: true
|
|
59
|
-
create-args:
|
|
60
|
-
python=${{ matrix.python-version }}
|
|
61
|
-
generate-run-shell: false
|
|
62
|
-
post-cleanup: none
|
|
63
|
-
- name: Install LabelGenerator # Install LabelGenerator manually to get the correct branch
|
|
64
|
-
run: |
|
|
65
|
-
pip install -e . --no-deps
|
|
66
|
-
shell: bash -el {0}
|
|
67
|
-
- name: Setup RAxML-NG and RAxML-NG env variable
|
|
68
|
-
uses: ./.github/actions/setup-raxmlng
|
|
69
|
-
- name: Setup IQ-TREE and IQ-TREE env variable
|
|
70
|
-
uses: ./.github/actions/setup-iqtree
|
|
71
|
-
- name: Test installation by running the CLI
|
|
72
|
-
uses: ./.github/actions/test-label-cli
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/MORPH.iqtree.iqtree
RENAMED
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.bestModel
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.bestTree
RENAMED
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.mlTrees
RENAMED
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.startTree
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/logs/test.rfdist.raxml.log
RENAMED
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.bestModel
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.bestTree
RENAMED
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.mlTrees
RENAMED
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.startTree
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.bestModel
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.bestTree
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.mlTrees
RENAMED
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.startTree
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.bestModel
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.bestTree
RENAMED
|
File without changes
|
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.log
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.mlTrees
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.rba
RENAMED
|
File without changes
|
{pythialabelgenerator-1.1.1 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.startTree
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|