Skip to content

fix: hoist reversions across a bifurcating root - #950

Merged
ivan-aksamentov merged 3 commits into
rustfrom
fix/optimize-root-hoisting-bifurcating-root
Sep 3, 2026
Merged

fix: hoist reversions across a bifurcating root#950
ivan-aksamentov merged 3 commits into
rustfrom
fix/optimize-root-hoisting-bifurcating-root

Conversation

@ivan-aksamentov

Copy link
Copy Markdown
Member

Update:

Problem: the reversion hoist reads only a node's own parent edge. At a bifurcating (degree-2) root, the two child edges are two halves of one unrooted edge, so a substitution a child reverts can lie on the sibling edge rather than the parent edge, and the per-node scan misses it. The tree keeps two mutations instead of one, and the count depends on root placement.

Fix: score each child against the parent edge combined with the inverted sibling substitutions, then move the root state at the reverted sites onto the sibling before the hoist removes the reversion. Nodes not directly under a bifurcating root are unchanged.

Correctness: the re-rooting preserves the total mutation count and runs only for sites the following hoist removes, so the potential strictly decreases on each change.

Result: the mutation count no longer depends on bifurcating-root placement, and equals the count obtained when the reversion lies on an internal edge. Applied only to degree-2 roots.

Work items

  • Augment reversion scoring for a bifurcating-root child with the inverted sibling substitutions
  • Re-root the reverted sites onto the sibling state, moving the root sequence and root reconstruction together
  • Wire detection and the slide into the per-polytomy hoist, leaving non-bifurcating-root behavior unchanged
  • Cover the cross-root case with unit, integration, and reroot-invariant property tests

Screenshots

optimize on the reported tree, rendered in auspice.us.

Before fix (9 mutations). Node A shows one change (C3T) and two reversions to root at positions 6 and 7.

Before: tree

Before: node A hover

Before: node A sequence changes

After fix (7 mutations). The shared substitutions moved to the ancestral branch and node A shows no sequence changes.

After: tree

After: node A sequence changes

- A degree-2 root is a reversible pass-through, so the substitution a child reverts can sit on the sibling edge, leaving the node's own parent edge empty at that site and hiding the reversion from per-node detection
- Score children against the parent edge augmented with the inverted sibling substitutions, then re-root the winning positions onto the parent edge so the existing hoist removes the reversion, making the move invariant to bifurcating-root placement
- Move the root clamp (root_sequence and the root node MAP) in lock-step so the next iteration's marginal reconstruction stays consistent with the rewritten edges
- Unit tests for the root slide: it moves the sibling substitution onto the parent edge and the root clamp, and the following hoist removes the exposed reversion
- Integration test on a bifurcating-root worked example reaching the one-mutation bipartition optimum
- Property test asserting the site cost is the reroot-invariant bipartition cost regardless of how many children carry the substitution, with the potential still falling on every change
- Document the bifurcating-root case in the reversion-resolution decision: the sibling edge can hold the substitution a child reverts, and the root slide re-roots the site so the standard hoist removes it, making the move invariant to bifurcating-root placement
- Update the test list to cite the new slide, worked-example, and reroot-invariant property coverage
@ivan-aksamentov
ivan-aksamentov merged commit 9555d80 into rust Sep 3, 2026
9 checks passed
@ivan-aksamentov
ivan-aksamentov deleted the fix/optimize-root-hoisting-bifurcating-root branch September 3, 2026 23:51
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