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.
Files changed (68) hide show
  1. mtrip-0.4.1/.clang-format +10 -0
  2. mtrip-0.4.1/.git-blame-ignore-revs +5 -0
  3. mtrip-0.4.1/.gitignore +94 -0
  4. mtrip-0.4.1/CLAUDE.md +109 -0
  5. mtrip-0.4.1/CMakeLists.txt +47 -0
  6. mtrip-0.4.1/PKG-INFO +215 -0
  7. mtrip-0.4.1/README.md +196 -0
  8. mtrip-0.4.1/lib/CMakeLists.txt +65 -0
  9. {mtrip-0.3.0/src/mtrip/_c_ext → mtrip-0.4.1/lib}/lookup_table.c +3 -23
  10. mtrip-0.4.1/lib/lookup_table.h +20 -0
  11. {mtrip-0.3.0/src/mtrip/_c_ext → mtrip-0.4.1/lib}/weights_omp.c +27 -55
  12. mtrip-0.4.1/lib/weights_omp.h +38 -0
  13. mtrip-0.4.1/pyproject.toml +112 -0
  14. mtrip-0.4.1/src/mtrip/__init__.py +3 -0
  15. {mtrip-0.3.0 → mtrip-0.4.1}/src/mtrip/bitsnbobs.pyx +6 -6
  16. mtrip-0.4.1/src/mtrip/cli/_common.py +159 -0
  17. mtrip-0.4.1/src/mtrip/cli/combine.py +115 -0
  18. mtrip-0.4.1/src/mtrip/cli/find.py +212 -0
  19. mtrip-0.3.0/src/mtrip/cli/mtrip_suboptimal_cmd.py → mtrip-0.4.1/src/mtrip/cli/suboptimal.py +129 -222
  20. {mtrip-0.3.0 → mtrip-0.4.1}/src/mtrip/median_tree_reconstruction.py +28 -26
  21. mtrip-0.4.1/src/mtrip/scipy_comb.pyx +50 -0
  22. {mtrip-0.3.0 → mtrip-0.4.1}/src/mtrip/snoob.py +1 -1
  23. {mtrip-0.3.0 → mtrip-0.4.1}/src/mtrip/triplet_omp_py.pyx +14 -20
  24. mtrip-0.4.1/tests/brute.py +139 -0
  25. mtrip-0.4.1/tests/common.py +9 -0
  26. mtrip-0.4.1/tests/test_bits.py +71 -0
  27. mtrip-0.4.1/tests/test_cli.py +269 -0
  28. mtrip-0.4.1/tests/test_median.py +60 -0
  29. mtrip-0.4.1/tests/test_newick.py +54 -0
  30. mtrip-0.4.1/tests/test_weights.py +50 -0
  31. mtrip-0.4.1/tests/test_weights_file.py +63 -0
  32. mtrip-0.4.1/uv.lock +338 -0
  33. mtrip-0.3.0/CMakeLists.txt +0 -12
  34. mtrip-0.3.0/MANIFEST.in +0 -19
  35. mtrip-0.3.0/Makefile +0 -203
  36. mtrip-0.3.0/PKG-INFO +0 -246
  37. mtrip-0.3.0/README.md +0 -225
  38. mtrip-0.3.0/pyproject.toml +0 -55
  39. mtrip-0.3.0/setup.cfg +0 -4
  40. mtrip-0.3.0/setup.py +0 -160
  41. mtrip-0.3.0/src/mtrip/__init__.py +0 -2
  42. mtrip-0.3.0/src/mtrip/_c_ext/CMakeLists.txt +0 -90
  43. mtrip-0.3.0/src/mtrip/_c_ext/lookup_table.h +0 -6
  44. mtrip-0.3.0/src/mtrip/_c_ext/weights_omp.h +0 -20
  45. mtrip-0.3.0/src/mtrip/bitsnbobs.c +0 -10334
  46. mtrip-0.3.0/src/mtrip/cli/__init__.py +0 -1
  47. mtrip-0.3.0/src/mtrip/cli/mtrip_cmd.py +0 -275
  48. mtrip-0.3.0/src/mtrip/cli/mtrip_combine_cmd.py +0 -93
  49. mtrip-0.3.0/src/mtrip/scipy_comb.c +0 -7098
  50. mtrip-0.3.0/src/mtrip/scipy_comb.pyx +0 -24
  51. mtrip-0.3.0/src/mtrip/triplet_omp_py.c +0 -30985
  52. mtrip-0.3.0/src/mtrip.egg-info/PKG-INFO +0 -246
  53. mtrip-0.3.0/src/mtrip.egg-info/SOURCES.txt +0 -41
  54. mtrip-0.3.0/src/mtrip.egg-info/dependency_links.txt +0 -1
  55. mtrip-0.3.0/src/mtrip.egg-info/entry_points.txt +0 -4
  56. mtrip-0.3.0/src/mtrip.egg-info/not-zip-safe +0 -1
  57. mtrip-0.3.0/src/mtrip.egg-info/requires.txt +0 -7
  58. mtrip-0.3.0/src/mtrip.egg-info/top_level.txt +0 -1
  59. mtrip-0.3.0/tests/test_bipartitions.py +0 -119
  60. mtrip-0.3.0/tests/test_cli.py +0 -123
  61. mtrip-0.3.0/tests/test_median_reconstruction.py +0 -101
  62. mtrip-0.3.0/tests/test_newick_parsing.py +0 -87
  63. mtrip-0.3.0/tests/test_triplet_omp.py +0 -109
  64. {mtrip-0.3.0 → mtrip-0.4.1}/LICENSE +0 -0
  65. {mtrip-0.3.0 → mtrip-0.4.1}/examples/example.nwk +0 -0
  66. {mtrip-0.3.0 → mtrip-0.4.1}/examples/example_5tips.nwk +0 -0
  67. {mtrip-0.3.0 → mtrip-0.4.1}/examples/large_example.nwk +0 -0
  68. /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
@@ -0,0 +1,5 @@
1
+ # Formatting-only commits; use with: git config blame.ignoreRevsFile .git-blame-ignore-revs
2
+ # ruff format
3
+ 3741393ce89326663f4dea0531a5854ddbc9d92d
4
+ # clang-format
5
+ 52c6118ba50292c3604fbe6585a649c95d6c1164
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).