Skip to content

feat: add BME objective and branch lengths - #232

Merged
Neclow merged 3 commits into
sbhattlab:mainfrom
Neclow:feat/bme-objective
Nov 6, 2026
Merged

Neclow merged 3 commits into
sbhattlab:mainfrom
Neclow:feat/bme-objective

Conversation

@Neclow

@Neclow Neclow commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator

Adds the balanced minimum evolution (BME) criterion, as a first step towards BME tree search in phylo2vec.opt.

  • bme_objective(tree, dm): Pauplin's BME length of the unrooted tree, L = sum_{i<j} 2^(1 - tau_ij) * dm[i, j], where tau_ij is the number of edges between leaves i and j.
  • bme_branch_lengths(tree, dm): FastME/Desper & Gascuel BME branch lengths, in the branch-length layout of a Phylo2Vec matrix. They sum to L and are returned unclamped: negative lengths signal a topology that fits dm poorly.

Both take a vector or a matrix (only the topology is used) and visit each leaf pair once, at its lowest common ancestor: O(n²) time, O(n) extra memory. Rust core in vector/objectives.rs, Python in phylo2vec.opt, R in R/opt.R.

Neclow and others added 3 commits November 6, 2026 15:27
Add vector/objectives.rs with:
- bme_objective: Pauplin's balanced minimum evolution (BME) length of
  the unrooted tree encoded by a vector, for a distance matrix D.
- bme_branch_lengths: FastME/Desper & Gascuel BME branch lengths, in
  the branch-length layout of a Phylo2Vec matrix. They sum to the BME
  length, are returned unclamped, and the edge through the root is
  split equally between the root's two child edges.

Both come from one pass that visits each leaf pair once, at its lowest
common ancestor, and spreads its weight over the edges of its path with
a difference array: O(n^2) time and O(n) extra memory.

Also ignore the local notes/ folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expose bme_objective and bme_branch_lengths in phylo2vec.opt. Both take
a Phylo2Vec vector or matrix (only the topology is used) and a distance
matrix, which must be n x n: otherwise they raise a ValueError instead
of a Rust panic. Branch lengths are returned unclamped, in the layout
of the branch lengths of a Phylo2Vec matrix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export bme_objective and bme_branch_lengths from a new R/opt.R,
mirroring phylo2vec.opt in Python. Both take a phylo2vec vector or
matrix (only the topology is used) and a distance matrix, whose shape
is checked in R so that a wrong one raises a readable error. Branch
lengths are returned unclamped, in the layout of the branch lengths of
a phylo2vec matrix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Neclow
Neclow merged commit 9a60c80 into sbhattlab:main Nov 6, 2026
9 checks passed
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