Skip to content

Faster, exact forward-backward; in-process seed anchoring; array-based progressive MSA - #9

Merged
glennhickey merged 1 commit into
masterfrom
faster-pecan
Oct 2, 2026
Merged

glennhickey merged 1 commit into
masterfrom
faster-pecan

Conversation

@glennhickey

Copy link
Copy Markdown

Summary

Makes pecan practical again as cactus bar's base aligner: faster and exact posteriors, better anchors, and a cheaper progressive MSA. Used by ComparativeGenomicsToolkit/cactus#2026, which pins this branch's commit (12ce978).

  • Forward-backward. Posteriors go through a probability-space version of the same banded, chunked recursion, with one log scale per diagonal and vectorised states (AVX2 gathers where available, a scalar path otherwise). It visits the same band and traceback points and computes the same posteriors as the log-space code, which stays for EM. It matches an exact reference to 1e-7. The old logAdd approximation was off by up to ~0.01. It is 5–11× faster per pairwise alignment. Chunks whose forward matrix would exceed 16 MB recompute blocks on the way back, so an unanchored 10 kb × 10 kb pair uses 116 MB instead of 3.9 GB.
  • Seed anchoring (PAIRWISE_ANCHOR_SEED): lastz's recipe, in process. It uses 12of19 spaced seeds that allow a transition, ungapped HOXD70 x-drop extension, and the heaviest colinear chain, recursing into the gaps. MUM anchors accept "unique" matches of about a dozen bases; at 0.3 substitutions per site per branch, half of them are off the true alignment. In simulation at that divergence, MSA precision/recall goes from 0.68/0.32 to 0.79/0.48. The library default is unchanged (PAIRWISE_ANCHOR_LEGACY); cactus selects seed in its config.
  • Progressive MSA works over arrays instead of hashes and AVL trees. The alignment and its order are unchanged, and it is 2.3× faster with half the memory on 6 × 100 kb. column_hashFn no longer puts one column of each sequence in every bucket.
  • Fixes: mum_destruct recursed once per chain element (now a loop); a zero-length HSP could enter the chain; threshold 0 now reports every cell, as the log path does. test_filterToRemoveOverlap compared each pair with itself, so it always expected an empty set.

Results in cactus (bar with baseAligner="pecan")

  • Evolver mammals (mafComparator): 0.859 / 0.654 → 0.869 / 0.666. bar time 41 s → 11 s.
  • 8 Mb of human/mouse, 8 threads: bar in 26 s and 0.5 GB.

Testing

  • New tests: the fast path against the log-space one, covering random bands, state machines, ragged ends, and unanchored pairs large enough to be recomputed in blocks. Also seed anchor validity.
  • cPecanLibTests passes, including under ASan. It also builds with the SSE/AVX macros undefined (the path ARM takes).
  • cactus's evolver tests and bar unit tests (in cactus#2026).

Please merge with a merge commit rather than squash/rebase, so the commit cactus pins stays reachable.

🤖 Generated with Claude Code

…d progressive MSA

Forward-backward.  The posterior probabilities (getPosteriorProbsWithBanding with
diagonalCalculationPosteriorMatchProbs or ...PosteriorProbs) now go through a
probability-space version of the same banded, chunked recursion.  Each diagonal
keeps a log scale that is moved only when its values drift out of [1e-100,1e100],
the state machine is flattened into arrays once and cached on the StateMachine,
and each state of a diagonal is one vectorised loop, with AVX2 gathers for the
emissions.  The band, the traceback points and which diagonals get posteriors in
which chunk are exactly those of the log-space code, which remains for EM.  It
matches an exact reference to 1e-7; the old logAdd, a piecewise cubic that drops
terms under e^-7.5, was off by up to ~0.01.  The chunks are planned from the band
before any dp, so buffers are sized once, and a chunk whose forward values would
pass 16MB keeps only a pair of diagonals before each block and recomputes the
block on the way back: an unanchored 10kb x 10kb pair drops from 3.9GB to 116MB.
5-11x faster per pairwise alignment.

Seed anchoring (anchorMethod = PAIRWISE_ANCHOR_SEED): lastz's recipe -- 12of19
spaced seeds allowing a transition, ungapped HOXD70 x-drop extension, the
heaviest colinear chain of the HSPs -- in process, recursing into the gaps with a
threshold that falls with their area.  MUM anchors accept "unique" matches of a
dozen bases; at 0.3 substitutions per site per branch half of them were off the
true alignment, and the banded dp followed them.  In simulation at that
divergence the MSA goes from 0.68/0.32 precision/recall with MUMs to 0.79/0.48.
The default is unchanged (PAIRWISE_ANCHOR_LEGACY); cactus selects seed.

Progressive MSA.  getMultipleSequenceAlignmentProgressive and
filterMultipleAlignedPairs work over arrays indexed by (sequence, position)
instead of hashes and AVL trees, with the weights in one block, and no longer
maintain the weight-ordered set only the greedy method reads.  Same alignment,
same order; 2.3x faster and half the memory on 6 x 100kb.  column_hashFn no longer
sums sequence and position, which made every bucket hold one of each sequence.

Also: mum_destruct is a loop rather than a recursion as deep as the MUM chain; a
zero-length HSP cannot enter the chain; threshold 0 reports every cell as the log
path does.  Tests: the fast path against the log-space one (random bands,
machines, ragged ends, and unanchored pairs big enough to be recomputed in
blocks) and seed anchor validity.  test_filterToRemoveOverlap compared each pair
with itself, so the set it expected was always empty; it now skips that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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