← 输入框|github.com|刷新
Skip to content

Score all sequence pairs without building alignments (GLOBAL, LOCAL): 27-286x faster getAllPairsScores - #1161

Open
mikepapadim wants to merge 1 commit into
biojava:masterfrom
mikepapadim:perf/all-pairs-scores
Open

mikepapadim wants to merge 1 commit into
biojava:masterfrom
mikepapadim:perf/all-pairs-scores

Conversation

@mikepapadim

Copy link
Copy Markdown

Follow-up to #1158, proposal (1): a CPU-only fast path, no new dependencies.

What

Alignments.getAllPairsScores built a full NeedlemanWunsch/SmithWaterman alignment for every pair, with a traceback matrix of objects per cell, only to read its score. So did the pairwise stage of getMultipleSequenceAlignment. For GLOBAL and LOCAL scoring the score needs only the last row of the dynamic-programming matrix.

The new package-private AllPairsScores scores each pair in linear memory on primitive arrays:

  • It uses the recurrences of AlignerHelper, linear and affine, so the scores are exactly the aligners'.
  • The scorers it returns for the guide tree have the aligners' maximum and minimum scores, hence the same distances and the same MSA.
  • Pairs run on the ConcurrencyTools pool, one task per query sequence, so ConcurrencyTools.setThreadPoolSize still applies.

Other scorer types (*_IDENTITIES, *_SIMILARITIES, KMERS, WU_MANBER), fewer than two sequences, an empty sequence or mixed compound sets take the existing path unchanged. No public API changes.

Speed

getAllPairsScores, BLOSUM62, gaps 10/1, best of 3 after 2 warm-ups. i9-13900K (32 threads), JDK 21, ConcurrencyTools default pool. The machine was also running other jobs, so read the ratios rather than the absolute times. The checksum of all scores is identical on both branches.

Input Pairs master this PR Speed-up
200 random proteins, 100–500 aa, GLOBAL 19,900 13.9 s 0.51 s 27×
same, LOCAL 19,900 13.1 s 0.33 s 40×
Pfam PF00104 (283 sequences), GLOBAL 39,903 552.6 s 1.93 s 286×

On PF00104, master is dominated by allocating and collecting the per-cell traceback objects of long sequences.

Tests

AllPairsScoresTest (8 tests). For every pair it compares score, max, min, distance and similarity against getAllPairsScorers + the aligners. Inputs:

  • random and related (mutated) proteins;
  • rare compounds (X B Z U *);
  • lengths from 1 to 300, including identical sequences;
  • DNA with NUC.4.4;
  • six gap penalties (linear and affine) and three matrices (BLOSUM62, BLOSUM30, PAM250).

It also checks that getMultipleSequenceAlignment gives the same profile as before for GLOBAL and LOCAL.

The whole biojava-alignment suite passes: 135 tests, 0 failures.

A bug this found in the existing path

SmithWaterman with a linear gap penalty throws ArrayIndexOutOfBoundsException for a pair that has no positive substitution score, e.g. W vs MK with BLOSUM62. The exception is in its traceback. getListFromFutures logs and drops the failed future, so runPairwiseScorers returns fewer scores than pairs, and every later score sits at the wrong index. The local score of such a pair is 0. That is what SmithWaterman returns with an affine penalty, and what this PR returns for both. noPositiveLocalCell documents it. I left SmithWaterman itself alone; happy to fix it in a separate PR if you like.

How to run

git fetch https://github.com/mikepapadim/biojava.git perf/all-pairs-scores && git checkout FETCH_HEAD
mvn -pl biojava-alignment -am install -DskipTests
mvn -pl biojava-alignment test -Dtest=AllPairsScoresTest                # parity with the aligners
mvn -pl biojava-alignment test                                          # whole module

To time it yourself, a minimal program (run it against master and this branch):

var seqs = new ArrayList<ProteinSequence>();          // e.g. FastaReaderHelper.readFastaProteinSequence(file).values()
var scores = Alignments.getAllPairsScores(seqs, PairwiseSequenceScorerType.GLOBAL,
        new SimpleGapPenalty(10, 1), SubstitutionMatrixHelper.getBlosum62());
ConcurrencyTools.shutdown();

Alignments.getAllPairsScores, and the pairwise stage of
getMultipleSequenceAlignment, built a full NeedlemanWunsch or SmithWaterman
alignment for every pair, with a traceback matrix of objects per cell, only
to read its score. For GLOBAL and LOCAL scoring the score needs only the last
row of the dynamic programming matrix.

AllPairsScores scores each pair in linear memory on primitive arrays, with
the recurrences of AlignerHelper (linear and affine gap penalties), so the
scores are exactly the aligners'. The scorers it returns for the guide tree
have the aligners' maximum and minimum scores, hence the same distances and
the same multiple alignment. Pairs run on the ConcurrencyTools pool, one task
per query sequence. Other scorer types, a single sequence, an empty sequence
or mixed compound sets take the existing path.

AllPairsScoresTest checks scores, maximum, minimum, distance and similarity
of every pair against the aligners for random and related proteins, rare
compounds, very different lengths, DNA, six gap penalties and three
matrices, and the multiple alignment for GLOBAL and LOCAL.

SmithWaterman with a linear gap penalty throws for a pair with no positive
substitution score (ArrayIndexOutOfBoundsException in its traceback), and
getListFromFutures drops that pair's score, so runPairwiseScorers returns
fewer scores than pairs. The local score of such a pair is 0, which is what
the new path returns; noPositiveLocalCell documents the case.
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