Skip to content

Upgrade to pyham 2 and ete4, make --species_tree optional - #48

Open
alpae wants to merge 5 commits into
DessimozLab:mainfrom
alpae:pyham2
Open

alpae wants to merge 5 commits into
DessimozLab:mainfrom
alpae:pyham2

Conversation

@alpae

@alpae alpae commented Oct 19, 2026

Copy link
Copy Markdown
Member

Part of #43: the pyham upgrade that should land before the algorithm fixes (#36, #37, #38).

Dependencies

  • pyham>=2.0.0rc1,<3 (to be raised to >=2.0.0 once pyham 2.0.0 is released) and ete4>=4.3,<5 instead of ete3
  • version 0.2.0
  • ete4 4.4.0 is only distributed as source (Cython), so installing it needs a C compiler. The Docker builder stage now installs gcc. The oma extra still pulls ete3 via pyoma; both packages can be installed side by side.

ete4 props

ete4 stores data as props of the tree nodes and does not support attribute access (node.genome) or setting arbitrary attributes (node.n_internal_nodes = ...). Consistently throughout edgehog:

  • results are stored with tree_node.add_prop(name, value) (was add_feature)
  • values edgehog always sets are read with tree_node.props[name]
  • the genome is read with tree_node.props.get('genome') (None replaces the former AttributeError)
  • is_leaf is a property
  • missing branch lengths are None in ete4 (1.0 in ete3); characterize_species_tree.branch_length counts them as 1.0, so the RED scores do not change

utils/extract_age_timetree.py is ported to ete4 as well.

Optional --species_tree

pyham 2 can read the species tree from a <taxonomy> section of the orthoXML (e.g. orthoXML exported from the OMA browser). --species_tree is therefore optional; if both are given, the species tree is used (pyham's behavior). Without either, edgehog exits with pyham's error message.

HOG ids

No change needed in edgehog. HOG ids are only used for output labels (duplicates are already made unique with _c<n>) and by the HDF5 writer, which strips the _<taxid> part to look up the OMA rows. pyham 2 gives HOGs that it synthesizes for paralogous branches their own LOFT ids, which makes the labels of in-paralogs distinguishable and the taxid lookup in get_taxid_from_hog_names more reliable.

Comparison with pyham 1

I ran test_data (GFF input, with --date_edges --orient_edges --phylostratify) and the OMA HDF5 test dataset (--hdf5, TSV and HDF5 output) with pyham 1.2.0 (current main) and with this branch, and compared all synteny graphs at every level. Each graph node was identified by its set of descendant extant proteins, so the comparison does not depend on HOG ids or node order.

  • HDF5 test dataset: all graphs identical (nodes, edges, weights, orientation, dating, RED scores); Synteny.h5 identical.
  • test_data: all edges and edge attributes identical. pyham 1 created 12 spurious ancestral genes without edges for families with duplications, e.g. it extended HOG:0001787_11, which the orthoXML roots at Hominidae, up to Simiiformes, and put an extra HOG at Cercopithecinae above the Simiiformes root of HOG:0001733_1. pyham 2 follows the orthoXML, so these singletons are no longer in the output.
  • The TSV files still differ textually: some labels changed (see HOG ids), edges are sometimes written as (gene2, gene1), and weight is written as 4 instead of 4.0 when a graph has no singletons.

With the <taxonomy> of the OMA orthoXML instead of simplified_speciestree.nwk, all graphs are identical, except that one level is named Saccharomycetales instead of Saccharomycotina.

Tests

  • tests converted to ete4 props
  • test_synthetic_pipeline.py now also runs with the species tree as <taxonomy> in the orthoXML and no tree file, with the same expected ancestral orders
  • new test_species_tree.py: RED scores and levels, missing branch lengths counting as 1.0, the optional --species_tree, the error without any taxonomy, and the newick taking precedence over the <taxonomy>
  • the 7 xfail tests still fail for the same reasons
  • full suite passes on Python 3.12 (macOS) and 3.9 (Linux container, ete4 built from source); docker build and a run of test_data in the image work

alpae added 5 commits October 19, 2026 15:50
pyham 2 uses ete4 instead of ete3. ete4 stores data as props of the
tree nodes and does not allow attribute access or setting arbitrary
attributes, so all synteny graphs and tree statistics are now stored
with add_prop and read from props; the genome of a tree node is
read with props.get('genome'). In ete4, missing branch lengths are
None instead of 1.0; characterize_tree counts them as 1.0, so the RED
scores do not change.

With pyham 2, the species tree can be read from the <taxonomy> section
of the orthoXML. --species_tree is therefore optional; if both are
given, the species tree is used.

On test_data and the OMA HDF5 test dataset, all synteny edges and
their attributes are unchanged compared to pyham 1. pyham 1 created a
few spurious ancestral genes for families with duplications (e.g. a
Hominidae HOG extended up to Simiiformes); pyham 2 does not, so these
singleton ancestral genes are gone. Labels of HOGs that pyham 2
synthesizes for paralogous branches now use their own ids (e.g.
HOG:0001787.1b instead of HOG:0001787_11).

Also:
- port utils/extract_age_timetree.py to ete4
- install gcc in the Docker builder stage, as ete4 is only
  distributed as source
- require pyham>=2.0.0rc1,<3 and ete4>=4.3,<5 instead of ete3
- bump the version to 0.2.0
Allow Python up to 3.14 and add 3.13 and 3.14 to the CI matrix. pyoma
requires numpy<2, which has no wheels for Python 3.13+, so the oma
extra is limited to Python <3.13 and numpy>=2 is required on 3.13+.

Python 3.15 runs as a non-blocking experimental CI job: it widens
requires-python in its checkout to the matrix version plus one minor
and resolves the dependencies without uv.lock.

Also add CLAUDE.md with notes for working on the repository.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant