PythiaLabelGenerator 1.0.0__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.
Files changed (72) hide show
  1. pythialabelgenerator-1.2.0/.gitattributes +1 -0
  2. pythialabelgenerator-1.2.0/.github/workflows/publish-pypi.yml +63 -0
  3. pythialabelgenerator-1.2.0/.github/workflows/test-label-generator.yml +89 -0
  4. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/.gitignore +3 -0
  5. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/.pre-commit-config.yaml +2 -2
  6. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/PKG-INFO +71 -24
  7. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/README.md +65 -18
  8. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/labelgenerator/iqtree.py +1 -2
  9. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/labelgenerator/label.py +6 -5
  10. pythialabelgenerator-1.2.0/labelgenerator/logger.py +49 -0
  11. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/labelgenerator/main.py +48 -19
  12. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/labelgenerator/raxmlng.py +9 -3
  13. pythialabelgenerator-1.2.0/pixi.toml +55 -0
  14. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/pyproject.toml +12 -31
  15. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/conftest.py +14 -3
  16. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/test_label.py +27 -11
  17. pythialabelgenerator-1.2.0/tests/test_main.py +138 -0
  18. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/test_raxmlng.py +38 -4
  19. pythialabelgenerator-1.0.0/.github/actions/setup-iqtree/action.yml +0 -22
  20. pythialabelgenerator-1.0.0/.github/actions/setup-raxmlng/action.yml +0 -20
  21. pythialabelgenerator-1.0.0/.github/actions/test-label-cli/action.yml +0 -12
  22. pythialabelgenerator-1.0.0/.github/workflows/test-label-generator.yml +0 -72
  23. pythialabelgenerator-1.0.0/etc/environment.yml +0 -9
  24. pythialabelgenerator-1.0.0/labelgenerator/logger.py +0 -32
  25. pythialabelgenerator-1.0.0/tests/test_config.py +0 -2
  26. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/LICENSE +0 -0
  27. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/__init__.py +0 -0
  28. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/examples/example.phy +0 -0
  29. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/labelgenerator/__init__.py +0 -0
  30. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/labelgenerator/iqtree_parser.py +0 -0
  31. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/setup.py +0 -0
  32. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/__init__.py +0 -0
  33. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/AA.phy +0 -0
  34. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/DNA.phy +0 -0
  35. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/MORPH.phy +0 -0
  36. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/AA.iqtree.iqtree +0 -0
  37. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/AA.iqtree.log +0 -0
  38. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/DNA.iqtree.iqtree +0 -0
  39. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/DNA.iqtree.log +0 -0
  40. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/DNA.raxml.log +0 -0
  41. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/MORPH.iqtree.iqtree +0 -0
  42. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/MORPH.iqtree.log +0 -0
  43. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.bestModel +0 -0
  44. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.bestTree +0 -0
  45. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.log +0 -0
  46. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.mlTrees +0 -0
  47. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.rba +0 -0
  48. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/test.raxml.startTree +0 -0
  49. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/test.rfdist.raxml.log +0 -0
  50. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/logs/test.rfdist.raxml.rfDistances +0 -0
  51. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.bestModel +0 -0
  52. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.bestTree +0 -0
  53. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.log +0 -0
  54. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.mlTrees +0 -0
  55. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.rba +0 -0
  56. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/AA.raxml.startTree +0 -0
  57. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.bestModel +0 -0
  58. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.bestTree +0 -0
  59. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.bestTreeCollapsed +0 -0
  60. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.log +0 -0
  61. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.mlTrees +0 -0
  62. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.rba +0 -0
  63. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/DNA.raxml.startTree +0 -0
  64. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.bestModel +0 -0
  65. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.bestTree +0 -0
  66. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.bestTreeCollapsed +0 -0
  67. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.log +0 -0
  68. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.mlTrees +0 -0
  69. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.rba +0 -0
  70. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/data/mltrees/MORPH.raxml.startTree +0 -0
  71. {pythialabelgenerator-1.0.0 → pythialabelgenerator-1.2.0}/tests/test_iqtree.py +0 -0
  72. {pythialabelgenerator-1.0.0 → 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
@@ -2,6 +2,9 @@
2
2
  **/*.log
3
3
  **/*.csv
4
4
 
5
+ # Pixi environments
6
+ .pixi/
7
+
5
8
  *.raxml.*
6
9
  !tests/**/*.raxml.*
7
10
 
@@ -1,13 +1,13 @@
1
1
  repos:
2
2
  - repo: https://github.com/pre-commit/pre-commit-hooks
3
- rev: v2.3.0
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.9.7
10
+ rev: v0.16.6
11
11
  hooks:
12
12
  # Run the linter.
13
13
  - id: ruff
@@ -1,28 +1,28 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: PythiaLabelGenerator
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: Command line tool to generate the ground-truth phylogenetic difficulty of MSAs
5
5
  Project-URL: Homepage, https://github.com/tschuelia/PythiaLabelGenerator
6
6
  Author-email: Julia Haag <info@juliaschmid.com>
7
7
  License-Expression: GPL-3.0-or-later
8
8
  License-File: LICENSE
9
- Classifier: Programming Language :: Python :: 3.9
10
- Classifier: Programming Language :: Python :: 3.10
11
9
  Classifier: Programming Language :: Python :: 3.11
12
10
  Classifier: Programming Language :: Python :: 3.12
13
- Requires-Python: <3.13,>=3.9
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Python: <3.15,>=3.11
14
14
  Requires-Dist: loguru
15
- Requires-Dist: pythiaphylopredictor>=2.0.0
15
+ Requires-Dist: pythiaphylopredictor>=2.1.0
16
16
  Requires-Dist: regex
17
17
  Provides-Extra: test
18
18
  Requires-Dist: pytest; extra == 'test'
19
19
  Description-Content-Type: text/markdown
20
20
 
21
- # Pythia Difficulty Label Generator
21
+ # Pythia Difficulty Label Generator (PyDLG)
22
22
 
23
23
  ![Label Generator GH actions CI](https://github.com/tschuelia/PythiaLabelGenerator/actions/workflows/test-label-generator.yml/badge.svg)
24
24
 
25
- The Pythia Difficulty Label Generator generates the ground-truth phylogenetic difficulty label for an MSA and
25
+ PyDLG generates the ground-truth phylogenetic difficulty label for an MSA and
26
26
  corresponds to the prediction target of our difficulty prediction tool [Pythia](https://github.com/tschuelia/PyPythia).
27
27
 
28
28
  > [!CAUTION]
@@ -59,6 +59,18 @@ Per default, the difficulty is based on $`N_{\text{all}}=100`$ ML trees.
59
59
  Note that this number can be adjusted by the user, however, the difficulty will only be an approximation if the number
60
60
  of trees is changed.
61
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
+
62
74
  ## Prediction of Phylogenetic Difficulty
63
75
 
64
76
  As stated above, computing the ground-truth difficulty for an MSA is very time-consuming and requires a lot of
@@ -70,7 +82,7 @@ comprising [SARS-CoV-2 sequences](https://doi.org/10.1093/molbev/msaa314) (appro
70
82
  12 hours on a large compute cluster. Using Pythia instead, we can predict the same MSA to be very difficult in about 2.5
71
83
  minutes on a standard MacBook.
72
84
 
73
- Only use this tool if you need the ground-truth difficulty for a specific MSA and you are sure that Pythia is unable to
85
+ Only use PyDLG if you need the ground-truth difficulty for a specific MSA and you are sure that Pythia is unable to
74
86
  predict the difficulty accurately.
75
87
  The only case where we observed Pythia to fail is for language MSAs, so if you are working with DNA, Protein, or
76
88
  biological morphological data, Pythia should work just fine 😉
@@ -79,16 +91,20 @@ biological morphological data, Pythia should work just fine 😉
79
91
 
80
92
  #### Requirements
81
93
 
82
- To use this labelling tool, you need to install
94
+ To use PyDLG, you need to install
83
95
 
84
96
  - RAxML-NG: See [the RAxML-NG GitHub repository](https://github.com/amkozlov/raxml-ng) for installation instructions.
85
- Please make sure that you install a RAxML-NG version < 2.
97
+ PyDLG supports both RAxML-NG 1.x and 2.x.
86
98
  - IQ-TREE: See [the IQ-TREE website](http://www.iqtree.org) for installation instructions. Please install IQ-TREE
87
99
  version 2 or higher.
88
100
 
89
101
  #### Install via conda (recommended)
90
102
 
91
- This package will soon be available on conda-forge :)
103
+ You can install the package using conda:
104
+
105
+ ```bash
106
+ conda install pythialabelgenerator -c conda-forge
107
+ ```
92
108
 
93
109
  #### Install using pip
94
110
 
@@ -98,9 +114,41 @@ You can install the package using pip:
98
114
  pip install pythialabelgenerator
99
115
  ```
100
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
+
101
149
  ## Usage
102
150
 
103
- This label-generator is primarily a command line tool. You can call it using the `label` command, for instance, to
151
+ PyDLG is primarily a command line tool. You can call it using the `label` command, for instance, to
104
152
  compute the difficulty for the example MSA provided in the `examples` directory run
105
153
 
106
154
  ```bash
@@ -112,9 +160,9 @@ to the console.
112
160
  The output will look something like this:
113
161
 
114
162
  ```text
115
- Difficulty LabelGenerator version 1.0.0 released by The Exelixis Lab
163
+ PyDLG version 1.2.0 released by The Exelixis Lab
116
164
  Developed by: Julia Haag
117
- Latest version: https://github.com/tschuelia/LabelGenerator
165
+ Latest version: https://github.com/tschuelia/PythiaLabelGenerator
118
166
  Questions/problems/suggestions? Please open an issue on GitHub.
119
167
 
120
168
  LabelGenerator was called at 06-Mar-2025 15:15:03 as follows:
@@ -142,14 +190,14 @@ Depending on your system setup, you might need to pass a RAxML-NG and IQ-TREE bi
142
190
  You can do this using the `-r` and `-i` options, respectively. This is required in case `raxml-ng` and/or `iqtree2` are
143
191
  not in your `$PATH`.
144
192
 
145
- Note that this `examply.phy` MSA is not the same exemplary MSA as we provide in the PyPythia repository, so please don't compare this ground-truth lable to the exemplary prediction in PyPythia 😉
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 😉
146
194
 
147
195
  For a full list of command line options, run `label -h`:
148
196
 
149
197
  ```text
150
- Difficulty LabelGenerator version 1.0.0 released by The Exelixis Lab
198
+ PyDLG version 1.2.0 released by The Exelixis Lab
151
199
  Developed by: Julia Haag
152
- Latest version: https://github.com/tschuelia/LabelGenerator
200
+ Latest version: https://github.com/tschuelia/PythiaLabelGenerator
153
201
  Questions/problems/suggestions? Please open an issue on GitHub.
154
202
 
155
203
  usage: label [-h] -m MSA -r RAXMLNG -i IQTREE [-t THREADS] [-s SEED] [-p PREFIX] [--model MODEL] [--ntrees NTREES] [--redo] [-V]
@@ -183,19 +231,19 @@ difficulties, as the difficulty is based on the average pairwise RF distance bet
183
231
 
184
232
  ### Result Files
185
233
 
186
- Running this labelling tool will result in the following files:
234
+ Running PyDLG will result in the following files:
187
235
 
188
236
  - `{prefix}.raxml.*`: RAxML-NG log and result files.
189
237
  - `{prefix}.iqtree.*`: IQ-TREE log and result files.
190
238
  - `{prefix}.labelGen.log`: Log file containing the output of the label generator. This is the same output as printed to
191
239
  the terminal.
240
+ - `{prefix}.csv`: The computed ground-truth difficulty and all features required for Pythia for the given MSA.
192
241
 
193
242
  You can set the prefix of these files using the `-p` option. By default, the prefix is the name of the MSA file.
194
243
  Note that RAxML-NG and IQ-TREE refuse to overwrite existing files. If you want to redo the computations, you can use the
195
244
  `--redo` option.
196
245
  Please also specify the `--redo` option if you want to change the number of trees to infer using the `--ntrees` option
197
- for the same prefix. Otherwise,
198
- the label generator will exit with an error message.
246
+ for the same prefix. Otherwise, PyDLG will exit with an error message.
199
247
 
200
248
  ### Input Data
201
249
 
@@ -209,7 +257,6 @@ you can do so using the `--model` option.
209
257
 
210
258
 
211
259
  ## Citation
212
- We will soon publish a pre-print on bioRxiv with updates on our Pythia difficulty prediction tool that will also include a
213
- brief description of this new labelling tool. Please cite this pre-print if you use this tool in your research.
260
+ Please cite the following preprint if you use this tool in your research.
214
261
 
215
- The link to the paper will be added soon 🙂
262
+ Haag, J. & Stamatakis, A. (2025). **Pythia 2.0: New Data, New Prediction Model, New Features.** *BioRxiv*. [https://doi.org/10.1101/2025.03.25.645182](https://doi.org/10.1101/2025.03.25.645182)
@@ -1,8 +1,8 @@
1
- # Pythia Difficulty Label Generator
1
+ # Pythia Difficulty Label Generator (PyDLG)
2
2
 
3
3
  ![Label Generator GH actions CI](https://github.com/tschuelia/PythiaLabelGenerator/actions/workflows/test-label-generator.yml/badge.svg)
4
4
 
5
- The Pythia Difficulty Label Generator generates the ground-truth phylogenetic difficulty label for an MSA and
5
+ PyDLG generates the ground-truth phylogenetic difficulty label for an MSA and
6
6
  corresponds to the prediction target of our difficulty prediction tool [Pythia](https://github.com/tschuelia/PyPythia).
7
7
 
8
8
  > [!CAUTION]
@@ -39,6 +39,18 @@ Per default, the difficulty is based on $`N_{\text{all}}=100`$ ML trees.
39
39
  Note that this number can be adjusted by the user, however, the difficulty will only be an approximation if the number
40
40
  of trees is changed.
41
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
+
42
54
  ## Prediction of Phylogenetic Difficulty
43
55
 
44
56
  As stated above, computing the ground-truth difficulty for an MSA is very time-consuming and requires a lot of
@@ -50,7 +62,7 @@ comprising [SARS-CoV-2 sequences](https://doi.org/10.1093/molbev/msaa314) (appro
50
62
  12 hours on a large compute cluster. Using Pythia instead, we can predict the same MSA to be very difficult in about 2.5
51
63
  minutes on a standard MacBook.
52
64
 
53
- Only use this tool if you need the ground-truth difficulty for a specific MSA and you are sure that Pythia is unable to
65
+ Only use PyDLG if you need the ground-truth difficulty for a specific MSA and you are sure that Pythia is unable to
54
66
  predict the difficulty accurately.
55
67
  The only case where we observed Pythia to fail is for language MSAs, so if you are working with DNA, Protein, or
56
68
  biological morphological data, Pythia should work just fine 😉
@@ -59,16 +71,20 @@ biological morphological data, Pythia should work just fine 😉
59
71
 
60
72
  #### Requirements
61
73
 
62
- To use this labelling tool, you need to install
74
+ To use PyDLG, you need to install
63
75
 
64
76
  - RAxML-NG: See [the RAxML-NG GitHub repository](https://github.com/amkozlov/raxml-ng) for installation instructions.
65
- Please make sure that you install a RAxML-NG version < 2.
77
+ PyDLG supports both RAxML-NG 1.x and 2.x.
66
78
  - IQ-TREE: See [the IQ-TREE website](http://www.iqtree.org) for installation instructions. Please install IQ-TREE
67
79
  version 2 or higher.
68
80
 
69
81
  #### Install via conda (recommended)
70
82
 
71
- This package will soon be available on conda-forge :)
83
+ You can install the package using conda:
84
+
85
+ ```bash
86
+ conda install pythialabelgenerator -c conda-forge
87
+ ```
72
88
 
73
89
  #### Install using pip
74
90
 
@@ -78,9 +94,41 @@ You can install the package using pip:
78
94
  pip install pythialabelgenerator
79
95
  ```
80
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
+
81
129
  ## Usage
82
130
 
83
- This label-generator is primarily a command line tool. You can call it using the `label` command, for instance, to
131
+ PyDLG is primarily a command line tool. You can call it using the `label` command, for instance, to
84
132
  compute the difficulty for the example MSA provided in the `examples` directory run
85
133
 
86
134
  ```bash
@@ -92,9 +140,9 @@ to the console.
92
140
  The output will look something like this:
93
141
 
94
142
  ```text
95
- Difficulty LabelGenerator version 1.0.0 released by The Exelixis Lab
143
+ PyDLG version 1.2.0 released by The Exelixis Lab
96
144
  Developed by: Julia Haag
97
- Latest version: https://github.com/tschuelia/LabelGenerator
145
+ Latest version: https://github.com/tschuelia/PythiaLabelGenerator
98
146
  Questions/problems/suggestions? Please open an issue on GitHub.
99
147
 
100
148
  LabelGenerator was called at 06-Mar-2025 15:15:03 as follows:
@@ -122,14 +170,14 @@ Depending on your system setup, you might need to pass a RAxML-NG and IQ-TREE bi
122
170
  You can do this using the `-r` and `-i` options, respectively. This is required in case `raxml-ng` and/or `iqtree2` are
123
171
  not in your `$PATH`.
124
172
 
125
- Note that this `examply.phy` MSA is not the same exemplary MSA as we provide in the PyPythia repository, so please don't compare this ground-truth lable to the exemplary prediction in PyPythia 😉
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 😉
126
174
 
127
175
  For a full list of command line options, run `label -h`:
128
176
 
129
177
  ```text
130
- Difficulty LabelGenerator version 1.0.0 released by The Exelixis Lab
178
+ PyDLG version 1.2.0 released by The Exelixis Lab
131
179
  Developed by: Julia Haag
132
- Latest version: https://github.com/tschuelia/LabelGenerator
180
+ Latest version: https://github.com/tschuelia/PythiaLabelGenerator
133
181
  Questions/problems/suggestions? Please open an issue on GitHub.
134
182
 
135
183
  usage: label [-h] -m MSA -r RAXMLNG -i IQTREE [-t THREADS] [-s SEED] [-p PREFIX] [--model MODEL] [--ntrees NTREES] [--redo] [-V]
@@ -163,19 +211,19 @@ difficulties, as the difficulty is based on the average pairwise RF distance bet
163
211
 
164
212
  ### Result Files
165
213
 
166
- Running this labelling tool will result in the following files:
214
+ Running PyDLG will result in the following files:
167
215
 
168
216
  - `{prefix}.raxml.*`: RAxML-NG log and result files.
169
217
  - `{prefix}.iqtree.*`: IQ-TREE log and result files.
170
218
  - `{prefix}.labelGen.log`: Log file containing the output of the label generator. This is the same output as printed to
171
219
  the terminal.
220
+ - `{prefix}.csv`: The computed ground-truth difficulty and all features required for Pythia for the given MSA.
172
221
 
173
222
  You can set the prefix of these files using the `-p` option. By default, the prefix is the name of the MSA file.
174
223
  Note that RAxML-NG and IQ-TREE refuse to overwrite existing files. If you want to redo the computations, you can use the
175
224
  `--redo` option.
176
225
  Please also specify the `--redo` option if you want to change the number of trees to infer using the `--ntrees` option
177
- for the same prefix. Otherwise,
178
- the label generator will exit with an error message.
226
+ for the same prefix. Otherwise, PyDLG will exit with an error message.
179
227
 
180
228
  ### Input Data
181
229
 
@@ -189,7 +237,6 @@ you can do so using the `--model` option.
189
237
 
190
238
 
191
239
  ## Citation
192
- We will soon publish a pre-print on bioRxiv with updates on our Pythia difficulty prediction tool that will also include a
193
- brief description of this new labelling tool. Please cite this pre-print if you use this tool in your research.
240
+ Please cite the following preprint if you use this tool in your research.
194
241
 
195
- The link to the paper will be added soon 🙂
242
+ Haag, J. & Stamatakis, A. (2025). **Pythia 2.0: New Data, New Prediction Model, New Features.** *BioRxiv*. [https://doi.org/10.1101/2025.03.25.645182](https://doi.org/10.1101/2025.03.25.645182)
@@ -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: Optional[int] = None,
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 parse_msa
3
+ from pypythia.msa import MSA, DataType
5
4
 
6
5
  from labelgenerator.iqtree import (
7
6
  filter_plausible_trees,
@@ -100,14 +99,15 @@ def get_label(
100
99
 
101
100
 
102
101
  def compute_label(
102
+ msa_obj: MSA,
103
103
  msa_file: pathlib.Path,
104
104
  raxmlng: pathlib.Path,
105
105
  iqtree: pathlib.Path,
106
106
  prefix: pathlib.Path,
107
- model: Optional[str] = None,
107
+ model: str | None = None,
108
108
  n_trees: int = 100,
109
109
  seed: int = 0,
110
- threads: Optional[int] = None,
110
+ threads: int | None = None,
111
111
  redo: bool = False,
112
112
  log_info: bool = True,
113
113
  ) -> float:
@@ -117,6 +117,7 @@ def compute_label(
117
117
 
118
118
 
119
119
  Args:
120
+ msa_obj (MSA): MSA object containing the input data.
120
121
  msa_file (pathlib.Path): Path to the MSA file to compute the label for. Can be either in FASTA or PHYLIP format.
121
122
  raxmlng (pathlib.Path): Path to the RAxML-NG executable.
122
123
  iqtree (pathlib.Path): Path to the IQ-TREE executable.
@@ -134,7 +135,6 @@ def compute_label(
134
135
  float: The ground truth difficulty label for the given MSA.
135
136
 
136
137
  """
137
- msa_obj = parse_msa(msa_file)
138
138
  model = model or msa_obj.get_raxmlng_model()
139
139
 
140
140
  # 1. Infer 100 ML trees for the given MSA using RAxML-NG
@@ -182,6 +182,7 @@ def compute_label(
182
182
  seed=seed,
183
183
  threads=threads,
184
184
  redo=redo,
185
+ is_morph=msa_obj.data_type == DataType.MORPH,
185
186
  )
186
187
 
187
188
  # 4. Filter the plausible trees
@@ -0,0 +1,49 @@
1
+ import sys
2
+ import textwrap
3
+ import time
4
+
5
+ import loguru
6
+
7
+ from labelgenerator import __version__
8
+
9
+ SCRIPT_START = time.perf_counter()
10
+
11
+
12
+ logger = loguru.logger
13
+ logger.remove()
14
+ logger.add(sys.stderr, format="{message}")
15
+
16
+
17
+ def get_header():
18
+ return textwrap.dedent(
19
+ f"PyDLG version {__version__} released by The Exelixis Lab\n"
20
+ f"Developed by: Julia Haag\n"
21
+ f"Latest version: https://github.com/tschuelia/PythiaLabelGenerator\n"
22
+ f"Questions/problems/suggestions? Please open an issue on GitHub.\n",
23
+ )
24
+
25
+
26
+ def log_runtime_information(message, log_runtime=True):
27
+ if log_runtime:
28
+ seconds = time.perf_counter() - SCRIPT_START
29
+ fmt_time = time.strftime("%H:%M:%S", time.gmtime(seconds))
30
+ time_string = f"[{fmt_time}] "
31
+ else:
32
+ time_string = ""
33
+ logger.info(f"{time_string}{message}")
34
+
35
+
36
+ def log_runtime(total_runtime: int, runtime_name: str):
37
+ hours, remainder = divmod(total_runtime, 3600)
38
+ minutes, seconds = divmod(remainder, 60)
39
+
40
+ if hours > 0:
41
+ logger.info(
42
+ f"{runtime_name}: {int(hours):02d}:{int(minutes):02d}:{seconds:02d} hours ({round(total_runtime)} seconds)."
43
+ )
44
+ elif minutes > 0:
45
+ logger.info(
46
+ f"{runtime_name}: {int(minutes):02d}:{int(seconds):02d} minutes ({round(total_runtime)} seconds)."
47
+ )
48
+ else:
49
+ logger.info(f"{runtime_name}: {seconds:.2f} seconds.")