Skip to content

feat: resolve reversion-driven polytomies in the optimize loop - #947

Merged
ivan-aksamentov merged 4 commits into
rustfrom
feat/optimize-polytomy-reversion-resolution
Sep 3, 2026
Merged

feat: resolve reversion-driven polytomies in the optimize loop#947
ivan-aksamentov merged 4 commits into
rustfrom
feat/optimize-polytomy-reversion-resolution

Conversation

@ivan-aksamentov

@ivan-aksamentov ivan-aksamentov commented Sep 3, 2026

Copy link
Copy Markdown
Member

Resolve:

Create:

Update:

A polytomy resolution can place a substitution on a parent edge and revert it on a child -- two mutations where one suffices. These reversions persisted. This adds a reversion hoist that removes them, run together with merge and helper-node cleanup as one fixpoint routine every iteration.

Work items

  • Add the reversion hoist: the parent-edge substitutions split into untouched, chained, and reverted sets, and the two relocated edges keep their keys via reparent_edge [src]
  • Fold merge, hoist, and helper retirement into one fixpoint routine run every iteration [src]
  • Guard the hoist so a node keeps at least one child, avoiding a childless stub [src]
  • Run polytomy resolution every loop iteration, independent of a zero-length collapse [src]
  • Promote merge_single_polytomy to pub(crate) for reuse by the routine [src]
  • Cover the move with unit, integration, and property tests, including the distance-preservation and potential-decrease invariants [src]

Possible improvements

- Add a reversion-driven hoist: when a child edge reverts a substitution on the parent edge, insert a node grouping the child with its sibling subtree and lift the non-reverted substitutions above it, removing one mutation per reversion and adding none. Branch lengths split proportionally to preserve root-to-node distances; indels use an all-or-nothing distance-preserving rule.
- Fold the shared-mutation merge, the hoist, and helper-node retirement into a single per-polytomy routine driven to a fixpoint by a monotone (mutation count, node count) potential. Run it every loop iteration, independent of the zero-optimal collapse, so reversions in the input and in polytomies formed by earlier iterations are resolved.
- Relocate the two moved edges with reparent_edge to keep their keys and partition state; register the fresh node and edge in dense partitions.
- Require the polytomy node to keep at least two children before hoisting: moving the reverting child under the new node must leave a sibling behind, or the node becomes a childless stub (a spurious leaf). A node whose children all revert the same position merges to a single reverting child and is now left with its residual reversion rather than dissolved.
- Unit-test the hoist move on constructed trees: the untouched substitutions land once above the new node without duplication, chained positions compose onto both output edges, pure reversions drop one mutation, distances are preserved, and per-partition splits are independent.
- Unit-test the indel rule for the cancelling, overlapping-fallback, and clean-hoist cases.
- Integration-test the merge -> hoist -> retire routine end to end: the worked example reaches the parsimony optimum, incompatible splits stop at the greedy bound, helper retirement spares pre-existing internal nodes, and the root and reversion-free polytomies are left untouched.
- Add a loop test proving the hoist fires through prune_and_merge_in_loop with no zero-optimal collapse.
- Property-test that the potential never rises and strictly falls on any change, and that leaves, non-negative branch lengths, and the single-root tree shape are preserved. The committed regression seed pins the all-children-revert degenerate case.
@ivan-aksamentov ivan-aksamentov changed the title feat/optimize polytomy reversion resolution feat: resolve reversion-driven polytomies in the optimize loop Sep 3, 2026
@ivan-aksamentov
ivan-aksamentov merged commit 020c1fc into rust Sep 3, 2026
9 checks passed
@ivan-aksamentov
ivan-aksamentov deleted the feat/optimize-polytomy-reversion-resolution branch September 3, 2026 06:28
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