Files
obitools4/pkg/obikmer/kmer_dissimilarity.go
T
Eric Coissac 3087c70daa Feature: Implement constrained k-mer generation and clique search
This commit introduces core functionality for oligonucleotide family tagging, constrained k-mer generation, and clique finding. It also includes updates to the Go language version and refactors the logic for k-mer dissimilarity metrics.
2026-11-03 09:25:34 +11:00

85 lines
2.2 KiB
Go

package obikmer
import (
"fmt"
"strings"
"sync"
)
// KmerMetric selects the distance used to compare two k-mers.
type KmerMetric int
const (
// HammingMetric counts the positions at which two k-mers differ.
HammingMetric KmerMetric = iota
// LCSMetric is the alignment length minus the LCS length (see KmerLCS).
LCSMetric
)
// ParseKmerMetric converts "hamming" or "lcs" (case insensitive) to a KmerMetric.
func ParseKmerMetric(name string) (KmerMetric, error) {
switch strings.ToLower(name) {
case "hamming":
return HammingMetric, nil
case "lcs":
return LCSMetric, nil
}
return HammingMetric, fmt.Errorf("unknown distance type %q (expected hamming or lcs)", name)
}
func (m KmerMetric) String() string {
if m == LCSMetric {
return "lcs"
}
return "hamming"
}
type lcsScratch struct {
partners []uint64
results []KmerLCSResult
}
// NewKmerDissimilarityFilter describes the graph whose vertices are kmers[i]
// (k-mers of size k, 1 <= k <= 31) and where two vertices are linked when
// their distance is at least dmin. The returned function has the signature of
// obigraph.NeighborFilter: it appends to out[:0] the candidates (indices in
// kmers) linked to v. It is safe for concurrent use.
func NewKmerDissimilarityFilter(kmers []uint64, k int, metric KmerMetric, dmin int) func(v int32, candidates, out []int32) []int32 {
if metric == HammingMetric {
return func(v int32, candidates, out []int32) []int32 {
q := kmers[v]
out = out[:0]
for _, c := range candidates {
if HammingDistance(q, kmers[c], k) >= dmin {
out = append(out, c)
}
}
return out
}
}
pool := sync.Pool{New: func() any { return new(lcsScratch) }}
return func(v int32, candidates, out []int32) []int32 {
s := pool.Get().(*lcsScratch)
defer pool.Put(s)
s.partners = s.partners[:0]
for _, c := range candidates {
s.partners = append(s.partners, kmers[c])
}
if cap(s.results) < len(candidates) {
s.results = make([]KmerLCSResult, len(candidates))
}
s.results = s.results[:len(candidates)]
KmerLCSBatch(kmers[v], s.partners, k, s.results)
out = out[:0]
for i, c := range candidates {
if s.results[i].Distance() >= dmin {
out = append(out, c)
}
}
return out
}
}