mtrip 0.3.0__tar.gz → 0.4.1__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.
- mtrip-0.4.1/.clang-format +10 -0
- mtrip-0.4.1/.git-blame-ignore-revs +5 -0
- mtrip-0.4.1/.gitignore +94 -0
- mtrip-0.4.1/CLAUDE.md +109 -0
- mtrip-0.4.1/CMakeLists.txt +47 -0
- mtrip-0.4.1/PKG-INFO +215 -0
- mtrip-0.4.1/README.md +196 -0
- mtrip-0.4.1/lib/CMakeLists.txt +65 -0
- {mtrip-0.3.0/src/mtrip/_c_ext → mtrip-0.4.1/lib}/lookup_table.c +3 -23
- mtrip-0.4.1/lib/lookup_table.h +20 -0
- {mtrip-0.3.0/src/mtrip/_c_ext → mtrip-0.4.1/lib}/weights_omp.c +27 -55
- mtrip-0.4.1/lib/weights_omp.h +38 -0
- mtrip-0.4.1/pyproject.toml +112 -0
- mtrip-0.4.1/src/mtrip/__init__.py +3 -0
- {mtrip-0.3.0 → mtrip-0.4.1}/src/mtrip/bitsnbobs.pyx +6 -6
- mtrip-0.4.1/src/mtrip/cli/_common.py +159 -0
- mtrip-0.4.1/src/mtrip/cli/combine.py +115 -0
- mtrip-0.4.1/src/mtrip/cli/find.py +212 -0
- mtrip-0.3.0/src/mtrip/cli/mtrip_suboptimal_cmd.py → mtrip-0.4.1/src/mtrip/cli/suboptimal.py +129 -222
- {mtrip-0.3.0 → mtrip-0.4.1}/src/mtrip/median_tree_reconstruction.py +28 -26
- mtrip-0.4.1/src/mtrip/scipy_comb.pyx +50 -0
- {mtrip-0.3.0 → mtrip-0.4.1}/src/mtrip/snoob.py +1 -1
- {mtrip-0.3.0 → mtrip-0.4.1}/src/mtrip/triplet_omp_py.pyx +14 -20
- mtrip-0.4.1/tests/brute.py +139 -0
- mtrip-0.4.1/tests/common.py +9 -0
- mtrip-0.4.1/tests/test_bits.py +71 -0
- mtrip-0.4.1/tests/test_cli.py +269 -0
- mtrip-0.4.1/tests/test_median.py +60 -0
- mtrip-0.4.1/tests/test_newick.py +54 -0
- mtrip-0.4.1/tests/test_weights.py +50 -0
- mtrip-0.4.1/tests/test_weights_file.py +63 -0
- mtrip-0.4.1/uv.lock +338 -0
- mtrip-0.3.0/CMakeLists.txt +0 -12
- mtrip-0.3.0/MANIFEST.in +0 -19
- mtrip-0.3.0/Makefile +0 -203
- mtrip-0.3.0/PKG-INFO +0 -246
- mtrip-0.3.0/README.md +0 -225
- mtrip-0.3.0/pyproject.toml +0 -55
- mtrip-0.3.0/setup.cfg +0 -4
- mtrip-0.3.0/setup.py +0 -160
- mtrip-0.3.0/src/mtrip/__init__.py +0 -2
- mtrip-0.3.0/src/mtrip/_c_ext/CMakeLists.txt +0 -90
- mtrip-0.3.0/src/mtrip/_c_ext/lookup_table.h +0 -6
- mtrip-0.3.0/src/mtrip/_c_ext/weights_omp.h +0 -20
- mtrip-0.3.0/src/mtrip/bitsnbobs.c +0 -10334
- mtrip-0.3.0/src/mtrip/cli/__init__.py +0 -1
- mtrip-0.3.0/src/mtrip/cli/mtrip_cmd.py +0 -275
- mtrip-0.3.0/src/mtrip/cli/mtrip_combine_cmd.py +0 -93
- mtrip-0.3.0/src/mtrip/scipy_comb.c +0 -7098
- mtrip-0.3.0/src/mtrip/scipy_comb.pyx +0 -24
- mtrip-0.3.0/src/mtrip/triplet_omp_py.c +0 -30985
- mtrip-0.3.0/src/mtrip.egg-info/PKG-INFO +0 -246
- mtrip-0.3.0/src/mtrip.egg-info/SOURCES.txt +0 -41
- mtrip-0.3.0/src/mtrip.egg-info/dependency_links.txt +0 -1
- mtrip-0.3.0/src/mtrip.egg-info/entry_points.txt +0 -4
- mtrip-0.3.0/src/mtrip.egg-info/not-zip-safe +0 -1
- mtrip-0.3.0/src/mtrip.egg-info/requires.txt +0 -7
- mtrip-0.3.0/src/mtrip.egg-info/top_level.txt +0 -1
- mtrip-0.3.0/tests/test_bipartitions.py +0 -119
- mtrip-0.3.0/tests/test_cli.py +0 -123
- mtrip-0.3.0/tests/test_median_reconstruction.py +0 -101
- mtrip-0.3.0/tests/test_newick_parsing.py +0 -87
- mtrip-0.3.0/tests/test_triplet_omp.py +0 -109
- {mtrip-0.3.0 → mtrip-0.4.1}/LICENSE +0 -0
- {mtrip-0.3.0 → mtrip-0.4.1}/examples/example.nwk +0 -0
- {mtrip-0.3.0 → mtrip-0.4.1}/examples/example_5tips.nwk +0 -0
- {mtrip-0.3.0 → mtrip-0.4.1}/examples/large_example.nwk +0 -0
- /mtrip-0.3.0/src/mtrip/py.typed → /mtrip-0.4.1/src/mtrip/cli/__init__.py +0 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# C style for lib/. clang-format is pinned in the dev dependency group so that
|
|
2
|
+
# local and CI output match:
|
|
3
|
+
# uv run clang-format -i lib/*.c lib/*.h
|
|
4
|
+
---
|
|
5
|
+
Language: Cpp
|
|
6
|
+
BasedOnStyle: LLVM
|
|
7
|
+
IndentWidth: 4
|
|
8
|
+
ColumnLimit: 80
|
|
9
|
+
# Preprocessor directives, including "#pragma omp", sit flush left.
|
|
10
|
+
IndentPPDirectives: None
|
mtrip-0.4.1/.gitignore
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Prerequisites
|
|
2
|
+
*.d
|
|
3
|
+
|
|
4
|
+
# Object files
|
|
5
|
+
*.o
|
|
6
|
+
*.ko
|
|
7
|
+
*.obj
|
|
8
|
+
*.elf
|
|
9
|
+
|
|
10
|
+
# Linker output
|
|
11
|
+
*.ilk
|
|
12
|
+
*.map
|
|
13
|
+
*.exp
|
|
14
|
+
|
|
15
|
+
# Precompiled Headers
|
|
16
|
+
*.gch
|
|
17
|
+
*.pch
|
|
18
|
+
|
|
19
|
+
# Libraries
|
|
20
|
+
*.lib
|
|
21
|
+
*.a
|
|
22
|
+
*.la
|
|
23
|
+
*.lo
|
|
24
|
+
|
|
25
|
+
# Shared objects (inc. Windows DLLs)
|
|
26
|
+
*.dll
|
|
27
|
+
*.so
|
|
28
|
+
*.so.*
|
|
29
|
+
*.dylib
|
|
30
|
+
|
|
31
|
+
# Executables
|
|
32
|
+
*.exe
|
|
33
|
+
*.out
|
|
34
|
+
*.app
|
|
35
|
+
*.i*86
|
|
36
|
+
*.x86_64
|
|
37
|
+
*.hex
|
|
38
|
+
|
|
39
|
+
# Debug files
|
|
40
|
+
*.dSYM/
|
|
41
|
+
*.su
|
|
42
|
+
*.idb
|
|
43
|
+
*.pdb
|
|
44
|
+
|
|
45
|
+
# Kernel Module Compile Results
|
|
46
|
+
*.mod*
|
|
47
|
+
*.cmd
|
|
48
|
+
.tmp_versions/
|
|
49
|
+
modules.order
|
|
50
|
+
Module.symvers
|
|
51
|
+
Mkfile.old
|
|
52
|
+
dkms.conf
|
|
53
|
+
|
|
54
|
+
# Useless data
|
|
55
|
+
*.dat
|
|
56
|
+
*.p
|
|
57
|
+
#
|
|
58
|
+
# These are generated by cython
|
|
59
|
+
bitsnbobs.c
|
|
60
|
+
scipy_comb.c
|
|
61
|
+
triplet_omp_py.c
|
|
62
|
+
|
|
63
|
+
# Goes without saying :)
|
|
64
|
+
__pycache__/
|
|
65
|
+
build/
|
|
66
|
+
dist/
|
|
67
|
+
.*_coal_unit
|
|
68
|
+
notebooks/
|
|
69
|
+
.ipynb_checkpoints/
|
|
70
|
+
*.swp
|
|
71
|
+
*.spec
|
|
72
|
+
src/mtrip.egg-info/
|
|
73
|
+
*.swo
|
|
74
|
+
.condaauto
|
|
75
|
+
|
|
76
|
+
# Python environments and tool caches
|
|
77
|
+
.venv/
|
|
78
|
+
venv/
|
|
79
|
+
.ruff_cache/
|
|
80
|
+
.pytest_cache/
|
|
81
|
+
*.egg-info/
|
|
82
|
+
|
|
83
|
+
# CMake build output (in-source builds)
|
|
84
|
+
CMakeFiles/
|
|
85
|
+
CMakeCache.txt
|
|
86
|
+
cmake_install.cmake
|
|
87
|
+
|
|
88
|
+
# Wheel builds
|
|
89
|
+
wheelhouse/
|
|
90
|
+
|
|
91
|
+
# Editors
|
|
92
|
+
.idea/
|
|
93
|
+
.vscode/
|
|
94
|
+
.DS_Store
|
mtrip-0.4.1/CLAUDE.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for Claude Code (claude.ai/code) when working in this repository.
|
|
4
|
+
|
|
5
|
+
## Project
|
|
6
|
+
|
|
7
|
+
`mtrip` finds exact median triplet trees: every rooted species tree that shares
|
|
8
|
+
the most triplets with a set of gene trees. It implements RTIST (Zhelezov &
|
|
9
|
+
Degnan, Bioinformatics 2022). The repo is `rtist`, the PyPI package and the
|
|
10
|
+
commands are `mtrip`. License: GPL-3.0-only.
|
|
11
|
+
|
|
12
|
+
Hybrid C / Cython / Python, built with CMake through scikit-build-core, and
|
|
13
|
+
developed with uv. Supported: Linux (glibc and musl) and macOS, x86_64 and
|
|
14
|
+
arm64, Python 3.10+. No Windows.
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv sync # dev env in .venv (editable install + dev tools)
|
|
20
|
+
uv run pytest # all tests
|
|
21
|
+
uv run pytest tests/test_cli.py -k quiet # a subset
|
|
22
|
+
uv run mtrip examples/example_5tips.nwk -n -p
|
|
23
|
+
uv run ruff format && uv run ruff check # Python
|
|
24
|
+
uv run clang-format -i lib/*.c lib/*.h # C (LLVM style, 4 spaces; see .clang-format)
|
|
25
|
+
uv build # sdist + wheel into dist/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`uv run` rebuilds the compiled modules automatically when a C, header, `.pyx`
|
|
29
|
+
or CMake file changes (`[tool.uv] cache-keys` in pyproject.toml). Build options:
|
|
30
|
+
`-Ccmake.define.MTRIP_OPENMP=OFF` (serial build) and
|
|
31
|
+
`-Ccmake.define.MTRIP_NATIVE=ON` (host-tuned, non-portable); in CI the serial
|
|
32
|
+
build uses `SKBUILD_CMAKE_DEFINE=MTRIP_OPENMP=OFF`. On macOS, OpenMP comes from
|
|
33
|
+
Homebrew's `libomp`, found automatically.
|
|
34
|
+
|
|
35
|
+
ruff and clang-format are pinned to exact versions in the `dev` dependency
|
|
36
|
+
group, and CI checks formatting with those versions; run them through `uv run`
|
|
37
|
+
so your output matches CI.
|
|
38
|
+
|
|
39
|
+
## Layout
|
|
40
|
+
|
|
41
|
+
- `lib/`: C core, built as the static library `ctriplet` (`lib/CMakeLists.txt`,
|
|
42
|
+
buildable on its own). `weights_omp.c` has the OpenMP-parallel
|
|
43
|
+
`fill_compressed_weight_representation`; `lookup_table.c` has the base-2 to
|
|
44
|
+
base-3 ("two2three") index table. Every OpenMP call is guarded by
|
|
45
|
+
`#ifdef _OPENMP` so the code also builds serially.
|
|
46
|
+
- `CMakeLists.txt`: transpiles and builds the three Cython extensions
|
|
47
|
+
(`mtrip.triplet_omp` from `triplet_omp_py.pyx`, `mtrip.bitsnbobs`,
|
|
48
|
+
`mtrip.scipycomb` from `scipy_comb.pyx`) with cython-cmake, and links
|
|
49
|
+
`triplet_omp` against `ctriplet`.
|
|
50
|
+
- `src/mtrip/median_tree_reconstruction.py`: the algorithm, in Python.
|
|
51
|
+
- `src/mtrip/cli/`: the three commands, `find.py` (`mtrip`), `suboptimal.py`
|
|
52
|
+
(`mtrip-suboptimal`), `combine.py` (`mtrip-combine`), with shared code in
|
|
53
|
+
`_common.py` (argument parsing, logging setup, colors, weights-file I/O).
|
|
54
|
+
- `tests/`: pytest. `brute.py` has independent brute-force reference
|
|
55
|
+
implementations; `common.py` has shared helpers (import it, there is no
|
|
56
|
+
conftest). Examples in `examples/` double as test inputs.
|
|
57
|
+
- `.github/workflows/`: `ci.yml` (lint + tests on 4 platforms + a no-OpenMP
|
|
58
|
+
build), `wheels.yml` (cibuildwheel; config in `[tool.cibuildwheel]`),
|
|
59
|
+
`publish.yml` (manual; uploads to PyPI/TestPyPI via Trusted Publishing after
|
|
60
|
+
CI and wheels pass).
|
|
61
|
+
|
|
62
|
+
## Algorithm pipeline
|
|
63
|
+
|
|
64
|
+
1. **Parse** (`simplify_nwk`, `get_names`): strip branch lengths and internal
|
|
65
|
+
labels; map each taxon to a bit, so a set of taxa is an int bitmask.
|
|
66
|
+
2. **Bipartitions** (`get_biparts`, `splitter`): each internal node of a gene
|
|
67
|
+
tree is a split `(a, b)` of disjoint bitmasks. `get_weights` (or
|
|
68
|
+
`get_weights_parallel`, multiprocessing) counts them over all gene trees;
|
|
69
|
+
`get_subset_biparts` groups them by `a | b`.
|
|
70
|
+
3. **Weight expansion** (C): `py_compressed_weight_rep` calls
|
|
71
|
+
`fill_compressed_weight_representation`, which computes, for every possible
|
|
72
|
+
bipartition of every subset, the number of gene-tree triplets it agrees
|
|
73
|
+
with. The array is indexed by a base-3 encoding of `(a, b)` (each taxon is
|
|
74
|
+
in a, in b, or neither), so it has about 2·3^(n−1) entries.
|
|
75
|
+
4. **Subset DP** (`get_stack`): over subsets in order of size (Gosper's hack,
|
|
76
|
+
`get_binary_subsets`), the best score of each subset is the best over its
|
|
77
|
+
splits of weight + best(a) + best(b). Every optimal split is kept.
|
|
78
|
+
5. **Reconstruction** (`get_all_trees`): walk the kept splits from the full set
|
|
79
|
+
down to leaves to enumerate *all* optimal trees.
|
|
80
|
+
|
|
81
|
+
`process_nwks` does steps 1–3; `median_triplet_trees` runs everything.
|
|
82
|
+
`mtrip-suboptimal` reuses the saved weights (step 3 output) to sample trees
|
|
83
|
+
whose score is within a fraction of the best.
|
|
84
|
+
|
|
85
|
+
## Conventions
|
|
86
|
+
|
|
87
|
+
- **stdout is for results only** (trees from `-p`, `mtrip-suboptimal`'s
|
|
88
|
+
output). Everything else goes through `logging` (loggers under `mtrip`;
|
|
89
|
+
the commands use `mtrip.cli`) to stderr. Library code never prints.
|
|
90
|
+
- Commands exit 0 on success, 1 on runtime errors, 2 on usage errors (use
|
|
91
|
+
`parser.error`).
|
|
92
|
+
- Color only when stderr is a terminal and `NO_COLOR` is unset (`style()` in
|
|
93
|
+
`_common.py`).
|
|
94
|
+
- Weights files are pickles with a fixed layout (`save_weights`/`load_weights`);
|
|
95
|
+
keep it backward compatible so old `.p` files still load.
|
|
96
|
+
- Results must not depend on the thread count or on whether OpenMP is used; the
|
|
97
|
+
tests check this against brute force.
|
|
98
|
+
- The version lives only in `pyproject.toml`; `mtrip.__version__` reads it from
|
|
99
|
+
the installed metadata.
|
|
100
|
+
- cysignals' `sig_on()` is compiled into `triplet_omp`, and its ABI changed in
|
|
101
|
+
1.12.6, so the module only works with cysignals from the side it was built
|
|
102
|
+
against. The cysignals requirements in `[build-system]` and `dependencies`
|
|
103
|
+
must stay identical (split at Python 3.13; see the comment in
|
|
104
|
+
pyproject.toml). The wheel tests install dependencies from wheels only, so a
|
|
105
|
+
pin without wheels for some platform fails there.
|
|
106
|
+
|
|
107
|
+
## Known limitations
|
|
108
|
+
|
|
109
|
+
- 20 or more taxa overflow the 32-bit indices in C (2·3^19 > INT_MAX).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Builds the mtrip Python package's compiled modules. Driven by
|
|
2
|
+
# scikit-build-core: build with `uv build` or `pip install .`, not by running
|
|
3
|
+
# cmake directly. (The C library alone can be built with cmake; see
|
|
4
|
+
# lib/CMakeLists.txt.)
|
|
5
|
+
cmake_minimum_required(VERSION 3.15...3.31)
|
|
6
|
+
project(mtrip LANGUAGES C)
|
|
7
|
+
|
|
8
|
+
if(NOT SKBUILD)
|
|
9
|
+
message(WARNING "This project is meant to be built through scikit-build-core, e.g. `uv build` or `pip install .`")
|
|
10
|
+
endif()
|
|
11
|
+
|
|
12
|
+
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
|
|
13
|
+
find_package(Cython MODULE REQUIRED VERSION 3.0)
|
|
14
|
+
include(UseCython)
|
|
15
|
+
|
|
16
|
+
add_subdirectory(lib)
|
|
17
|
+
|
|
18
|
+
# cysignals ships its .pxd files and C headers inside its Python package.
|
|
19
|
+
execute_process(
|
|
20
|
+
COMMAND "${Python_EXECUTABLE}" -c "import cysignals, os; print(os.path.dirname(cysignals.__file__))"
|
|
21
|
+
OUTPUT_VARIABLE CYSIGNALS_DIR
|
|
22
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
23
|
+
RESULT_VARIABLE _cysignals_result
|
|
24
|
+
)
|
|
25
|
+
if(NOT _cysignals_result EQUAL 0)
|
|
26
|
+
message(FATAL_ERROR "mtrip: cysignals must be importable at build time")
|
|
27
|
+
endif()
|
|
28
|
+
get_filename_component(CYSIGNALS_PARENT_DIR "${CYSIGNALS_DIR}" DIRECTORY)
|
|
29
|
+
|
|
30
|
+
# mtrip_cython_module(<module> <pyx file>): build src/mtrip/<pyx> as mtrip.<module>.
|
|
31
|
+
function(mtrip_cython_module module pyx)
|
|
32
|
+
cython_transpile("src/mtrip/${pyx}"
|
|
33
|
+
LANGUAGE C
|
|
34
|
+
MODULE_NAME "mtrip.${module}"
|
|
35
|
+
INCLUDE_DIRECTORIES "${CYSIGNALS_PARENT_DIR}"
|
|
36
|
+
OUTPUT_VARIABLE c_source
|
|
37
|
+
)
|
|
38
|
+
python_add_library(${module} MODULE "${c_source}" WITH_SOABI)
|
|
39
|
+
target_link_libraries(${module} PRIVATE mtrip_build_flags)
|
|
40
|
+
install(TARGETS ${module} DESTINATION mtrip)
|
|
41
|
+
endfunction()
|
|
42
|
+
|
|
43
|
+
mtrip_cython_module(bitsnbobs bitsnbobs.pyx)
|
|
44
|
+
mtrip_cython_module(scipycomb scipy_comb.pyx)
|
|
45
|
+
mtrip_cython_module(triplet_omp triplet_omp_py.pyx)
|
|
46
|
+
target_link_libraries(triplet_omp PRIVATE ctriplet)
|
|
47
|
+
target_include_directories(triplet_omp PRIVATE "${CYSIGNALS_DIR}")
|
mtrip-0.4.1/PKG-INFO
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mtrip
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: Find exact median triplet trees (phylogenetics)
|
|
5
|
+
Author-Email: Gleb Zhelezov <gleb@glebzh.com>
|
|
6
|
+
License-Expression: GPL-3.0-only
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: C
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Operating System :: MacOS
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
13
|
+
Project-URL: Homepage, https://github.com/glebzhelezov/rtist
|
|
14
|
+
Project-URL: Issues, https://github.com/glebzhelezov/rtist/issues
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: cysignals<1.12.6,>=1.12.3; python_version < "3.13"
|
|
17
|
+
Requires-Dist: cysignals>=1.13; python_version >= "3.13"
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# mtrip
|
|
21
|
+
|
|
22
|
+
mtrip finds exact median triplet trees. You give it a set of rooted gene trees,
|
|
23
|
+
and it finds every species tree that shares as many rooted triplets with them
|
|
24
|
+
as possible. The paper below calls the method RTIST; the package and its
|
|
25
|
+
commands are called `mtrip`.
|
|
26
|
+
|
|
27
|
+
> Gleb Zhelezov and James H. Degnan. [*Trying out a million genes to find the
|
|
28
|
+
> perfect pair with RTIST*](https://academic.oup.com/bioinformatics/article/38/14/3565/6596045).
|
|
29
|
+
> Bioinformatics 38(14):3565-3573, 2022.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install mtrip
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
or run it without installing, using [uv](https://docs.astral.sh/uv/):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uvx mtrip genes.nwk
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
pip installs a prebuilt wheel on Linux (x86_64 and arm64, Alpine included) and
|
|
44
|
+
on macOS 11 or newer (Intel and Apple Silicon), for Python 3.10 and up. The
|
|
45
|
+
wheels come with OpenMP built in, so mtrip uses all your CPU cores and you
|
|
46
|
+
don't need to install anything else. On Windows, use
|
|
47
|
+
[WSL](https://learn.microsoft.com/windows/wsl/). Anywhere else, pip compiles
|
|
48
|
+
mtrip from source (see [Building from source](#building-from-source)).
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
`genes.nwk` has one rooted gene tree per line, in Newick format:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
((A,B),(C,(D,E)));
|
|
56
|
+
(((A,C),B),(D,E));
|
|
57
|
+
(((A,D),B),(C,E));
|
|
58
|
+
((A,E),((B,C),D));
|
|
59
|
+
(A,(B,((C,D),E)));
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```console
|
|
63
|
+
$ mtrip genes.nwk -n -p
|
|
64
|
+
...
|
|
65
|
+
Best possible triplet count is 26, out of a maximum of 50.
|
|
66
|
+
...
|
|
67
|
+
((A,B),((C,D),E));
|
|
68
|
+
((A,B),(C,(D,E)));
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Each of these trees shares 26 triplets with the gene trees, and no tree shares
|
|
72
|
+
more, so both are median triplet trees. When several trees tie for the best
|
|
73
|
+
score, mtrip reports all of them.
|
|
74
|
+
|
|
75
|
+
## Commands
|
|
76
|
+
|
|
77
|
+
### `mtrip`: median triplet trees
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
mtrip genes.nwk # writes the trees to out_genes.nwk
|
|
81
|
+
mtrip genes.nwk trees.nwk # ...or to a file you name
|
|
82
|
+
mtrip genes.nwk -n -p # print the trees instead of saving them
|
|
83
|
+
mtrip genes.nwk -b weights.p # also save the weights, for the commands below
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The input file has one Newick string per line. mtrip ignores branch lengths
|
|
87
|
+
and internal node labels, and skips blank lines and lines starting with `#`.
|
|
88
|
+
By default it uses all CPUs; `-t N` limits it to N threads.
|
|
89
|
+
|
|
90
|
+
### `mtrip-suboptimal`: near-optimal trees
|
|
91
|
+
|
|
92
|
+
This samples trees whose score is close to the best one, using a weights file
|
|
93
|
+
saved with `mtrip -b`:
|
|
94
|
+
|
|
95
|
+
```console
|
|
96
|
+
$ mtrip-suboptimal weights.p -f 0.9 -n 5
|
|
97
|
+
#26
|
|
98
|
+
((A,B),((C,D),E));
|
|
99
|
+
#25
|
|
100
|
+
((A,B),(D,(C,E)));
|
|
101
|
+
#24
|
|
102
|
+
(A,(B,(C,(D,E))));
|
|
103
|
+
...
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The trees come out highest score first, each with its score on the line
|
|
107
|
+
above. Set the cutoff with `-f`, as a fraction of the best score (default
|
|
108
|
+
0.99), or with `-m`, as an absolute score. `-n` caps the number of trees
|
|
109
|
+
(default 100), and `-s` seeds the random sampling.
|
|
110
|
+
|
|
111
|
+
### `mtrip-combine`: combine runs
|
|
112
|
+
|
|
113
|
+
Weights from separate runs (for example, on parts of a large data set) add up.
|
|
114
|
+
Combine them, then work with the result as if it came from one run:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
mtrip part1.nwk -b part1.p
|
|
118
|
+
mtrip part2.nwk -b part2.p
|
|
119
|
+
mtrip-combine part1.p part2.p -o all.p
|
|
120
|
+
mtrip-suboptimal all.p -f 1 # the median trees of the combined data
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
All inputs must have the same taxa.
|
|
124
|
+
|
|
125
|
+
> [!NOTE]
|
|
126
|
+
> Weights files are Python pickles, and loading a pickle can run arbitrary
|
|
127
|
+
> code. Only load weights files you created or trust. `mtrip-suboptimal` asks
|
|
128
|
+
> before loading one; `-y` skips the question.
|
|
129
|
+
|
|
130
|
+
All three commands take `-h` (the full list of options), `-v` (version) and
|
|
131
|
+
`-q` (quiet).
|
|
132
|
+
|
|
133
|
+
## Output and scripting
|
|
134
|
+
|
|
135
|
+
The commands write only their results to standard output: the trees from
|
|
136
|
+
`-p`, and the scored trees from `mtrip-suboptimal`. Progress and other messages
|
|
137
|
+
go to standard error, so you can redirect or pipe the results:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
mtrip genes.nwk -n -p > median_trees.nwk
|
|
141
|
+
mtrip genes.nwk -n -p -q | some_other_tool # -q: no progress messages
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Messages are in color on a terminal, unless the `NO_COLOR` environment
|
|
145
|
+
variable is set. The exit code is `0` on success, `1` on an error such as
|
|
146
|
+
unreadable input or a malformed Newick string, and `2` on invalid command-line
|
|
147
|
+
arguments.
|
|
148
|
+
|
|
149
|
+
## Limits
|
|
150
|
+
|
|
151
|
+
The search is exact, so its cost grows exponentially with the number of taxa
|
|
152
|
+
*n* (it stores about 2·3<sup>n−1</sup> weights): about 1 GB of memory at 18
|
|
153
|
+
taxa and about 3 GB at 19. The number of gene trees matters much less. Inputs
|
|
154
|
+
with 20 or more taxa are not supported yet.
|
|
155
|
+
|
|
156
|
+
## Building from source
|
|
157
|
+
|
|
158
|
+
You need a C compiler and CMake 3.15 or newer. The build fetches its Python
|
|
159
|
+
build dependencies (Cython, cysignals, scikit-build-core) itself.
|
|
160
|
+
|
|
161
|
+
OpenMP is optional but makes the main computation parallel. On Linux, GCC
|
|
162
|
+
supports it out of the box. On macOS, install `libomp` with Homebrew
|
|
163
|
+
(`brew install libomp`); the build finds it automatically. Without OpenMP the
|
|
164
|
+
build still succeeds and runs single-threaded.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git clone https://github.com/glebzhelezov/rtist.git
|
|
168
|
+
cd rtist
|
|
169
|
+
pip install .
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Build options, passed through pip (or `uv pip`) with `-C`:
|
|
173
|
+
|
|
174
|
+
- `-Ccmake.define.MTRIP_NATIVE=ON` tunes the code for the CPU you're building
|
|
175
|
+
on. It runs faster, but only on CPUs like yours.
|
|
176
|
+
- `-Ccmake.define.MTRIP_OPENMP=OFF` builds without OpenMP even if it's
|
|
177
|
+
available.
|
|
178
|
+
|
|
179
|
+
## Development
|
|
180
|
+
|
|
181
|
+
The project uses [uv](https://docs.astral.sh/uv/):
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
uv sync # .venv with mtrip (editable) and dev tools
|
|
185
|
+
uv run pytest # test suite
|
|
186
|
+
uv run mtrip examples/example_5tips.nwk -n -p
|
|
187
|
+
uv run ruff format && uv run ruff check # Python formatting and lint
|
|
188
|
+
uv run clang-format -i lib/*.c lib/*.h # C formatting
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Edits to `.py` files take effect immediately. When a C, `.pyx` or CMake file
|
|
192
|
+
changes, the next `uv run` rebuilds the compiled modules.
|
|
193
|
+
|
|
194
|
+
On every push, GitHub Actions runs the lint checks and the tests on Linux and
|
|
195
|
+
macOS (x86_64 and arm64), and builds and tests the wheels. The `Publish`
|
|
196
|
+
workflow uploads releases to PyPI using Trusted Publishing.
|
|
197
|
+
|
|
198
|
+
## Citation
|
|
199
|
+
|
|
200
|
+
```bibtex
|
|
201
|
+
@article{zhelezov2022trying,
|
|
202
|
+
title={Trying out a million genes to find the perfect pair with {RTIST}},
|
|
203
|
+
author={Zhelezov, Gleb and Degnan, James H},
|
|
204
|
+
journal={Bioinformatics},
|
|
205
|
+
volume={38},
|
|
206
|
+
number={14},
|
|
207
|
+
pages={3565--3573},
|
|
208
|
+
year={2022},
|
|
209
|
+
publisher={Oxford University Press}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
GPL-3.0. See [LICENSE](https://github.com/glebzhelezov/rtist/blob/HEAD/LICENSE).
|
mtrip-0.4.1/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# mtrip
|
|
2
|
+
|
|
3
|
+
mtrip finds exact median triplet trees. You give it a set of rooted gene trees,
|
|
4
|
+
and it finds every species tree that shares as many rooted triplets with them
|
|
5
|
+
as possible. The paper below calls the method RTIST; the package and its
|
|
6
|
+
commands are called `mtrip`.
|
|
7
|
+
|
|
8
|
+
> Gleb Zhelezov and James H. Degnan. [*Trying out a million genes to find the
|
|
9
|
+
> perfect pair with RTIST*](https://academic.oup.com/bioinformatics/article/38/14/3565/6596045).
|
|
10
|
+
> Bioinformatics 38(14):3565-3573, 2022.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install mtrip
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
or run it without installing, using [uv](https://docs.astral.sh/uv/):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uvx mtrip genes.nwk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
pip installs a prebuilt wheel on Linux (x86_64 and arm64, Alpine included) and
|
|
25
|
+
on macOS 11 or newer (Intel and Apple Silicon), for Python 3.10 and up. The
|
|
26
|
+
wheels come with OpenMP built in, so mtrip uses all your CPU cores and you
|
|
27
|
+
don't need to install anything else. On Windows, use
|
|
28
|
+
[WSL](https://learn.microsoft.com/windows/wsl/). Anywhere else, pip compiles
|
|
29
|
+
mtrip from source (see [Building from source](#building-from-source)).
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
`genes.nwk` has one rooted gene tree per line, in Newick format:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
((A,B),(C,(D,E)));
|
|
37
|
+
(((A,C),B),(D,E));
|
|
38
|
+
(((A,D),B),(C,E));
|
|
39
|
+
((A,E),((B,C),D));
|
|
40
|
+
(A,(B,((C,D),E)));
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```console
|
|
44
|
+
$ mtrip genes.nwk -n -p
|
|
45
|
+
...
|
|
46
|
+
Best possible triplet count is 26, out of a maximum of 50.
|
|
47
|
+
...
|
|
48
|
+
((A,B),((C,D),E));
|
|
49
|
+
((A,B),(C,(D,E)));
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Each of these trees shares 26 triplets with the gene trees, and no tree shares
|
|
53
|
+
more, so both are median triplet trees. When several trees tie for the best
|
|
54
|
+
score, mtrip reports all of them.
|
|
55
|
+
|
|
56
|
+
## Commands
|
|
57
|
+
|
|
58
|
+
### `mtrip`: median triplet trees
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
mtrip genes.nwk # writes the trees to out_genes.nwk
|
|
62
|
+
mtrip genes.nwk trees.nwk # ...or to a file you name
|
|
63
|
+
mtrip genes.nwk -n -p # print the trees instead of saving them
|
|
64
|
+
mtrip genes.nwk -b weights.p # also save the weights, for the commands below
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The input file has one Newick string per line. mtrip ignores branch lengths
|
|
68
|
+
and internal node labels, and skips blank lines and lines starting with `#`.
|
|
69
|
+
By default it uses all CPUs; `-t N` limits it to N threads.
|
|
70
|
+
|
|
71
|
+
### `mtrip-suboptimal`: near-optimal trees
|
|
72
|
+
|
|
73
|
+
This samples trees whose score is close to the best one, using a weights file
|
|
74
|
+
saved with `mtrip -b`:
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
$ mtrip-suboptimal weights.p -f 0.9 -n 5
|
|
78
|
+
#26
|
|
79
|
+
((A,B),((C,D),E));
|
|
80
|
+
#25
|
|
81
|
+
((A,B),(D,(C,E)));
|
|
82
|
+
#24
|
|
83
|
+
(A,(B,(C,(D,E))));
|
|
84
|
+
...
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The trees come out highest score first, each with its score on the line
|
|
88
|
+
above. Set the cutoff with `-f`, as a fraction of the best score (default
|
|
89
|
+
0.99), or with `-m`, as an absolute score. `-n` caps the number of trees
|
|
90
|
+
(default 100), and `-s` seeds the random sampling.
|
|
91
|
+
|
|
92
|
+
### `mtrip-combine`: combine runs
|
|
93
|
+
|
|
94
|
+
Weights from separate runs (for example, on parts of a large data set) add up.
|
|
95
|
+
Combine them, then work with the result as if it came from one run:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
mtrip part1.nwk -b part1.p
|
|
99
|
+
mtrip part2.nwk -b part2.p
|
|
100
|
+
mtrip-combine part1.p part2.p -o all.p
|
|
101
|
+
mtrip-suboptimal all.p -f 1 # the median trees of the combined data
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
All inputs must have the same taxa.
|
|
105
|
+
|
|
106
|
+
> [!NOTE]
|
|
107
|
+
> Weights files are Python pickles, and loading a pickle can run arbitrary
|
|
108
|
+
> code. Only load weights files you created or trust. `mtrip-suboptimal` asks
|
|
109
|
+
> before loading one; `-y` skips the question.
|
|
110
|
+
|
|
111
|
+
All three commands take `-h` (the full list of options), `-v` (version) and
|
|
112
|
+
`-q` (quiet).
|
|
113
|
+
|
|
114
|
+
## Output and scripting
|
|
115
|
+
|
|
116
|
+
The commands write only their results to standard output: the trees from
|
|
117
|
+
`-p`, and the scored trees from `mtrip-suboptimal`. Progress and other messages
|
|
118
|
+
go to standard error, so you can redirect or pipe the results:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
mtrip genes.nwk -n -p > median_trees.nwk
|
|
122
|
+
mtrip genes.nwk -n -p -q | some_other_tool # -q: no progress messages
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Messages are in color on a terminal, unless the `NO_COLOR` environment
|
|
126
|
+
variable is set. The exit code is `0` on success, `1` on an error such as
|
|
127
|
+
unreadable input or a malformed Newick string, and `2` on invalid command-line
|
|
128
|
+
arguments.
|
|
129
|
+
|
|
130
|
+
## Limits
|
|
131
|
+
|
|
132
|
+
The search is exact, so its cost grows exponentially with the number of taxa
|
|
133
|
+
*n* (it stores about 2·3<sup>n−1</sup> weights): about 1 GB of memory at 18
|
|
134
|
+
taxa and about 3 GB at 19. The number of gene trees matters much less. Inputs
|
|
135
|
+
with 20 or more taxa are not supported yet.
|
|
136
|
+
|
|
137
|
+
## Building from source
|
|
138
|
+
|
|
139
|
+
You need a C compiler and CMake 3.15 or newer. The build fetches its Python
|
|
140
|
+
build dependencies (Cython, cysignals, scikit-build-core) itself.
|
|
141
|
+
|
|
142
|
+
OpenMP is optional but makes the main computation parallel. On Linux, GCC
|
|
143
|
+
supports it out of the box. On macOS, install `libomp` with Homebrew
|
|
144
|
+
(`brew install libomp`); the build finds it automatically. Without OpenMP the
|
|
145
|
+
build still succeeds and runs single-threaded.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
git clone https://github.com/glebzhelezov/rtist.git
|
|
149
|
+
cd rtist
|
|
150
|
+
pip install .
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Build options, passed through pip (or `uv pip`) with `-C`:
|
|
154
|
+
|
|
155
|
+
- `-Ccmake.define.MTRIP_NATIVE=ON` tunes the code for the CPU you're building
|
|
156
|
+
on. It runs faster, but only on CPUs like yours.
|
|
157
|
+
- `-Ccmake.define.MTRIP_OPENMP=OFF` builds without OpenMP even if it's
|
|
158
|
+
available.
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
The project uses [uv](https://docs.astral.sh/uv/):
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
uv sync # .venv with mtrip (editable) and dev tools
|
|
166
|
+
uv run pytest # test suite
|
|
167
|
+
uv run mtrip examples/example_5tips.nwk -n -p
|
|
168
|
+
uv run ruff format && uv run ruff check # Python formatting and lint
|
|
169
|
+
uv run clang-format -i lib/*.c lib/*.h # C formatting
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Edits to `.py` files take effect immediately. When a C, `.pyx` or CMake file
|
|
173
|
+
changes, the next `uv run` rebuilds the compiled modules.
|
|
174
|
+
|
|
175
|
+
On every push, GitHub Actions runs the lint checks and the tests on Linux and
|
|
176
|
+
macOS (x86_64 and arm64), and builds and tests the wheels. The `Publish`
|
|
177
|
+
workflow uploads releases to PyPI using Trusted Publishing.
|
|
178
|
+
|
|
179
|
+
## Citation
|
|
180
|
+
|
|
181
|
+
```bibtex
|
|
182
|
+
@article{zhelezov2022trying,
|
|
183
|
+
title={Trying out a million genes to find the perfect pair with {RTIST}},
|
|
184
|
+
author={Zhelezov, Gleb and Degnan, James H},
|
|
185
|
+
journal={Bioinformatics},
|
|
186
|
+
volume={38},
|
|
187
|
+
number={14},
|
|
188
|
+
pages={3565--3573},
|
|
189
|
+
year={2022},
|
|
190
|
+
publisher={Oxford University Press}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
GPL-3.0. See [LICENSE](https://github.com/glebzhelezov/rtist/blob/HEAD/LICENSE).
|