Files

111 lines
2.9 KiB
Go
Raw Permalink Normal View History

package obialign
import (
"fmt"
"math/rand"
"testing"
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq"
)
// randomDNA returns a random ACGT sequence of length n.
func randomDNA(rng *rand.Rand, n int) []byte {
const nuc = "ACGT"
s := make([]byte, n)
for i := range s {
s[i] = nuc[rng.Intn(4)]
}
return s
}
// mutate returns a copy of s in which each position is, with probability
// rate, substituted, deleted or followed by an inserted base (equiprobable).
func mutate(rng *rand.Rand, s []byte, rate float64) []byte {
const nuc = "ACGT"
out := make([]byte, 0, len(s)+len(s)/10)
for _, c := range s {
if rng.Float64() >= rate {
out = append(out, c)
continue
}
switch rng.Intn(3) {
case 0:
out = append(out, nuc[rng.Intn(4)])
case 1: // deletion
case 2:
out = append(out, c, nuc[rng.Intn(4)])
}
}
return out
}
// benchPairs builds a fixed set of pairs. "similar" pairs differ by about
// 2% of mutated positions, "random" pairs are unrelated sequences.
func benchPairs(n, length int, similar bool) [][2]*obiseq.BioSequence {
rng := rand.New(rand.NewSource(42))
pairs := make([][2]*obiseq.BioSequence, n)
for i := range pairs {
a := randomDNA(rng, length)
var b []byte
if similar {
b = mutate(rng, a, 0.02)
} else {
b = randomDNA(rng, length)
}
pairs[i] = [2]*obiseq.BioSequence{
obiseq.NewBioSequence("a", a, ""),
obiseq.NewBioSequence("b", b, ""),
}
}
return pairs
}
// BenchmarkLegacyLCS measures the historical implementation (the one
// FastLCSScore used before BandedLCS), kept as the "before" reference.
func BenchmarkLegacyLCS(b *testing.B) {
for _, similar := range []bool{true, false} {
kind := "random"
if similar {
kind = "similar"
}
for _, length := range []int{100, 500, 2000} {
pairs := benchPairs(64, length, similar)
for _, maxErr := range []int{-1, 5, length / 20, length / 5} {
name := fmt.Sprintf("%s/len=%d/maxErr=%d", kind, length, maxErr)
b.Run(name, func(b *testing.B) {
var buffer []uint64
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
p := pairs[i%len(pairs)]
FastLCSEGFScoreByte(p[0].Sequence(), p[1].Sequence(), maxErr, false, &buffer)
}
})
}
}
}
}
// BenchmarkBandedLCS runs on the same pairs as BenchmarkLegacyLCS.
func BenchmarkBandedLCS(b *testing.B) {
for _, similar := range []bool{true, false} {
kind := "random"
if similar {
kind = "similar"
}
for _, length := range []int{100, 500, 2000} {
pairs := benchPairs(64, length, similar)
for _, maxErr := range []int{-1, 5, length / 20, length / 5} {
name := fmt.Sprintf("%s/len=%d/maxErr=%d", kind, length, maxErr)
b.Run(name, func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
p := pairs[i%len(pairs)]
BandedLCS(p[0].Sequence(), p[1].Sequence(), maxErr)
}
})
}
}
}
}