Skip to content

fix(build): make graph construction invariant under input FASTA order - #190

Draft
mmolari wants to merge 9 commits into
masterfrom
debug/order-dependence
Draft

fix(build): make graph construction invariant under input FASTA order#190
mmolari wants to merge 9 commits into
masterfrom
debug/order-dependence

Conversation

@mmolari

@mmolari mmolari commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

pangraph build gave a different graph depending only on the order the input FASTA files were listed.

Block ids derive from input position, are passed to minimap2 as sequence names, and -X (MM_F_NO_DUAL) picks the direction of each pair by strcmp on those names. Argument order thus
decided query vs reference for every pair, and assign_anchor_block privileged the reference, so its consensus won and propagated into the merged block. Independently, neighbor
joining resolved Q-matrix ties by row order — which for three taxa is always tied, since every off-diagonal entry is identically -(D₁₂+D₁₃+D₂₃) — so the inferred topology also
followed the command line.

Changes

  • align/block_names.rs: new BlockNames, naming and ordering blocks by (xxhash64(consensus), block_id) as zero-padded {hash}_{id}, so the aligner input is a pure function of block
    content. Both backends feed blocks in this order and recover ids by lookup.
  • Tie-breaks in filter_matches and assign_anchor_block now use that content key instead of vector order / reference-first.
  • Neighbor joining sorts its leaves by a content-derived key.
  • par_bridge() → indexed par_iter().zip(), which preserves order by construction.
  • tests/itest_build_order_invariance.rs: invariance over six argument permutations under three topologies, plus guide-tree sibling swap.

Guarantee: graph structure is a pure function of the input sequence set, the tree topology and the alignment parameters. BlockId values still derive from fasta.index, so output JSON
is not byte-identical across orders — only the structure is.

Validation

All six permutations of the reproducer now give 512 blocks (was 452–509) with byte-identical consensus and depth multisets, and the same under a sibling-swapped tree. Without
--guide-tree they likewise collapse to one answer. Each test assertion was checked against reverted code. Fixtures (data/test_graph.json, plasmids.json) are unchanged; tests and
clippy are clean.

Full investigation, measurements and rejected alternatives: notes/n00_build_order_dependence.typ.

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