Installation
============
Installing forestpd
-------------------
forestpd requires `Python `_ 3.10 or newer. Install it from PyPI:
.. code-block:: bash
pip install forestpd
or, for development, as an editable install from a clone of the
`GitHub repository `_:
.. code-block:: bash
git clone https://github.com/nholtgrefe/forestpd
cd forestpd
pip install -e .
Requirements
~~~~~~~~~~~~
pip installs the two required dependencies:
* `NumPy `_ >= 1.20, for the trees and the dynamic-programming tables.
* `Numba `_ >= 0.56, which compiles the Newick parser, the cluster
reduction and the dynamic program. Every compiled kernel has a NumPy/Python fallback with the
same results. The fallback is used, with a :class:`~forestpd.ForestPDWarning` on import, if
Numba cannot be imported, and is several times slower.
Optional dependencies
~~~~~~~~~~~~~~~~~~~~~
.. list-table::
:widths: 20 80
:header-rows: 1
* - Extra
- Contents
* - ``phylozoo``
- Conversion to and from `PhyloZoo `_ trees, for
plotting and the other PhyloZoo tools (see :ref:`manual-phylozoo`).
* - ``maffe``
- `maffe `_, for minimum agreement forests (see
:ref:`installation-maffe`).
* - ``dev``
- pytest, black, ruff, mypy and PhyloZoo.
* - ``docs``
- Sphinx and the documentation theme.
.. code-block:: bash
pip install "forestpd[phylozoo]"
pip install "forestpd[maffe]"
pip install -e ".[dev]" # in a clone
Building the documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~
In a clone:
.. code-block:: bash
pip install -e ".[docs]"
sphinx-build -b html docs/source docs/build/html
The pages are then in ``docs/build/html/index.html``.
.. _installation-maffe:
Exact agreement forests with maffe
----------------------------------
The default agreement-forest stage, :class:`~forestpd.maf.HeuristicMAF`, needs no other software.
The exact stages :class:`~forestpd.maf.MaffeRMAF` and :class:`~forestpd.maf.MaffeUMAF`, and
:class:`~forestpd.maf.TieredMAF` (the heuristic, with an exact forest where it is large), need
`maffe `_, a C++ solver for minimum agreement forests with Python
bindings.
Installing maffe
~~~~~~~~~~~~~~~~
.. code-block:: bash
pip install "forestpd[maffe]"
This installs maffe from PyPI, which has ready-built packages for Python 3.11 to 3.14 on Linux and
Windows (x86_64). maffe needs Python >= 3.11.
forestpd works without maffe. maffe is only loaded when a :class:`~forestpd.maf.MaffeRMAF`,
:class:`~forestpd.maf.MaffeUMAF` or :class:`~forestpd.maf.TieredMAF` is created; without maffe,
this raises a :class:`~forestpd.ForestPDImportError` that gives the install command.
Building maffe from source
~~~~~~~~~~~~~~~~~~~~~~~~~~
On other platforms, such as macOS, Linux on ARM, or Linux with glibc older than 2.28, PyPI has no
package that fits, and maffe is built from source into the same Python environment:
.. code-block:: bash
pip install "maffe @ git+https://github.com/roehrt/maffe@v0.1.1"
The build needs Python >= 3.11, a C++23 compiler (GCC >= 14 or a recent Clang), CMake, Ninja, Git
and ``patch``, and downloads and compiles SCIP and HiGHS. It takes about ten minutes. The commands
below pass build options with ``-Csetup-args``, which needs pip >= 23.1.
**Older compilers.** If the system compiler is too old (e.g. GCC 12 on Debian 12), use a GCC 14
toolchain from conda-forge. Link its C++ runtime statically, so that maffe keeps its own copy and
does not clash with other compiled packages:
.. code-block:: bash
mamba create -p ./cxx14 -c conda-forge "gxx_linux-64=14" cmake ninja pkg-config patch
export PATH="$PWD/cxx14/bin:$PATH"
export CC=x86_64-conda-linux-gnu-gcc CXX=x86_64-conda-linux-gnu-g++
pip install "maffe @ git+https://github.com/roehrt/maffe@v0.1.1" \
"-Csetup-args=-Dcpp_link_args=-static-libstdc++ -static-libgcc -Wl,--exclude-libs,ALL -Wl,-Bsymbolic"
On Debian and Ubuntu, conda's compiler does not search the system's multiarch headers, so the
build can fail with ``x86_64-linux-gnu/python3.11/pyconfig.h: No such file or directory``. Make
only that folder visible, then build again:
.. code-block:: bash
mkdir -p pyinc/x86_64-linux-gnu
ln -s /usr/include/x86_64-linux-gnu/python3.11 pyinc/x86_64-linux-gnu/python3.11
export CPATH="$PWD/pyinc${CPATH:+:$CPATH}"
**Gurobi.** maffe can use Gurobi instead of HiGHS as SCIP's LP solver (build option ``gurobi``;
Gurobi needs a licence). With Gurobi in ``$GUROBI_HOME`` (e.g. ``.../gurobi1301/linux64``):
.. code-block:: bash
mkdir -p grblib && ln -sf "$GUROBI_HOME/lib/libgurobi130.so" grblib/libgurobi.so
export GUROBI_DIR="$GUROBI_HOME" CPATH="$GUROBI_HOME/include${CPATH:+:$CPATH}"
export LIBRARY_PATH="$PWD/grblib:$GUROBI_HOME/lib"
pip install --force-reinstall "maffe @ git+https://github.com/roehrt/maffe@v0.1.1" \
-Csetup-args=-Dgurobi=enabled "-Csetup-args=-Dcpp_link_args=-Wl,-rpath,$GUROBI_HOME/lib"
Checking the installation
-------------------------
Test run
~~~~~~~~
Import forestpd, print the version and solve a small instance. The latest version is |version|.
.. code-block:: python
import forestpd as fpd
print(fpd.__version__) # the installed version
print(fpd.optimize_maxmpd(["(A:1,B:1,(C:1,D:1):2);", "(A:1,C:1,(B:1,D:1):2);"], budget=2).value)
# 8.0
To check that maffe is found:
.. code-block:: python
from forestpd.maf import TieredMAF
TieredMAF() # raises ForestPDImportError without maffe
Troubleshooting
~~~~~~~~~~~~~~~
**The first call is slow.** Numba compiles the kernels the first time they run in a Python session
(a few seconds). Later calls in the same session are fast, and the compiled code is cached on disk
for later sessions. When timing forestpd, solve a small instance first.
**A ForestPDWarning on import.** Numba could not be imported, so forestpd uses its slower
fallbacks. Install ``numba`` (a regular dependency) into the same environment.
**A ForestPDWarning about rooted trees.** The trees look rooted (a ``[&R]`` comment, PhyloZoo
``DirectedPhyNetwork`` trees, or a binary root in every tree) while PD is unrooted, the default.
Pass ``rooted=True`` for rooted PD, or ignore the warning; see :ref:`manual-rooted`.