mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-11-09 09:24:41 +00:00
113 lines
3.3 KiB
Go
113 lines
3.3 KiB
Go
package obikmer_test
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"math/rand"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obigraph"
|
||
|
|
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obikmer"
|
||
|
|
)
|
||
|
|
|
||
|
|
func allKmers(k int) []uint64 {
|
||
|
|
kmers := make([]uint64, 0, 1<<(2*uint(k)))
|
||
|
|
for km := range obikmer.IterConstrainedKmers(k, obikmer.NoKmerConstraints()) {
|
||
|
|
kmers = append(kmers, km)
|
||
|
|
}
|
||
|
|
return kmers
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestDissimilarityFilterMatchesDistances(t *testing.T) {
|
||
|
|
const k = 7
|
||
|
|
rng := rand.New(rand.NewSource(1))
|
||
|
|
kmers := make([]uint64, 300)
|
||
|
|
for i := range kmers {
|
||
|
|
kmers[i] = rng.Uint64() & (1<<(2*k) - 1)
|
||
|
|
}
|
||
|
|
candidates := make([]int32, 0, len(kmers))
|
||
|
|
for i := 1; i < len(kmers); i++ {
|
||
|
|
candidates = append(candidates, int32(i))
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, metric := range []obikmer.KmerMetric{obikmer.HammingMetric, obikmer.LCSMetric} {
|
||
|
|
for _, dmin := range []int{1, 3, 5} {
|
||
|
|
filter := obikmer.NewKmerDissimilarityFilter(kmers, k, metric, dmin)
|
||
|
|
got := filter(0, candidates, nil)
|
||
|
|
|
||
|
|
var want []int32
|
||
|
|
for _, c := range candidates {
|
||
|
|
var d int
|
||
|
|
if metric == obikmer.HammingMetric {
|
||
|
|
d = obikmer.HammingDistance(kmers[0], kmers[c], k)
|
||
|
|
} else {
|
||
|
|
d = obikmer.KmerLCS(kmers[0], kmers[c], k).Distance()
|
||
|
|
}
|
||
|
|
if d >= dmin {
|
||
|
|
want = append(want, c)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if len(got) != len(want) {
|
||
|
|
t.Fatalf("%v d>=%d: got %d vertices, want %d", metric, dmin, len(got), len(want))
|
||
|
|
}
|
||
|
|
for i := range got {
|
||
|
|
if got[i] != want[i] {
|
||
|
|
t.Fatalf("%v d>=%d: mismatch at %d", metric, dmin, i)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// A family of 5-mers pairwise at Hamming distance >= 3, built with FindClique.
|
||
|
|
func TestFindClique_HammingFamily(t *testing.T) {
|
||
|
|
const k, dmin, target = 5, 3, 32
|
||
|
|
kmers := allKmers(k)
|
||
|
|
filter := obikmer.NewKmerDissimilarityFilter(kmers, k, obikmer.HammingMetric, dmin)
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
r := obigraph.FindClique(ctx, len(kmers), filter, target, obigraph.CliqueOptions{Workers: 2})
|
||
|
|
if !r.Reached {
|
||
|
|
t.Fatalf("family of %d not found (best %d, exhausted %v)", target, len(r.Clique), r.Exhausted)
|
||
|
|
}
|
||
|
|
for i, a := range r.Clique {
|
||
|
|
for _, b := range r.Clique[i+1:] {
|
||
|
|
if obikmer.HammingDistance(kmers[a], kmers[b], k) < dmin {
|
||
|
|
t.Fatalf("%d and %d are too close", kmers[a], kmers[b])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFindClique_LCSFamily(t *testing.T) {
|
||
|
|
const k, dmin, target = 6, 3, 20
|
||
|
|
kmers := allKmers(k)
|
||
|
|
filter := obikmer.NewKmerDissimilarityFilter(kmers, k, obikmer.LCSMetric, dmin)
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
r := obigraph.FindClique(ctx, len(kmers), filter, target, obigraph.CliqueOptions{Workers: 2})
|
||
|
|
if !r.Reached {
|
||
|
|
t.Fatalf("family of %d not found (best %d, exhausted %v)", target, len(r.Clique), r.Exhausted)
|
||
|
|
}
|
||
|
|
for i, a := range r.Clique {
|
||
|
|
for _, b := range r.Clique[i+1:] {
|
||
|
|
if obikmer.KmerLCS(kmers[a], kmers[b], k).Distance() < dmin {
|
||
|
|
t.Fatalf("%d and %d are too close", kmers[a], kmers[b])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestParseKmerMetric(t *testing.T) {
|
||
|
|
if m, err := obikmer.ParseKmerMetric("LCS"); err != nil || m != obikmer.LCSMetric {
|
||
|
|
t.Fatal("lcs not parsed")
|
||
|
|
}
|
||
|
|
if m, err := obikmer.ParseKmerMetric("hamming"); err != nil || m != obikmer.HammingMetric {
|
||
|
|
t.Fatal("hamming not parsed")
|
||
|
|
}
|
||
|
|
if _, err := obikmer.ParseKmerMetric("levenshtein"); err == nil {
|
||
|
|
t.Fatal("unknown metric must be refused")
|
||
|
|
}
|
||
|
|
}
|