mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-11-09 09:24:41 +00:00
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.
85 lines
2.2 KiB
Go
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
|
|
}
|
|
}
|