mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-11-10 01:24:41 +00:00
Introduce new sequence matching logic and corpus handling
This commit introduces the core matching functionality, including parallel corpus loading, the `Corpus` data structure, and implementations for `matchNaive` and `matchApat`. It also defines new matching rules, variant handling, and comprehensive unit and integration tests to validate the new logic and parameter handling.
This commit is contained in:
1 parent
f75e774bd5
commit
ad065f6853
10 files changed
+2265
-59
No files matched your search
@@ -0,0 +1,78 @@
|
||||
package obiprimer
|
||||
|
||||
import "git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq"
|
||||
|
||||
// baseCode maps a nucleotide to 0..3 (A,C,G,T; U is T) and everything else to 4,
|
||||
// which never equals a word base.
|
||||
var baseCode = func() (t [256]byte) {
|
||||
for i := range t {
|
||||
t[i] = 4
|
||||
}
|
||||
for i, c := range "ACGT" {
|
||||
t[c], t[c+'a'-'A'] = byte(i), byte(i)
|
||||
}
|
||||
t['U'], t['u'] = 3, 3
|
||||
return
|
||||
}()
|
||||
|
||||
func codesOf(s string) []byte {
|
||||
out := make([]byte, len(s))
|
||||
for i := range out {
|
||||
out[i] = baseCode[s[i]]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// matchNaive is the reference implementation: every window of every sequence
|
||||
// is compared with every word, base by base. It is slow and exists to define
|
||||
// the exact semantics (see MatchParams) that faster implementations must
|
||||
// reproduce.
|
||||
func matchNaive(c *Corpus, words []uint64, p MatchParams) ([]WordMatches, error) {
|
||||
if err := p.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fwd, rev := wordStrings(words, p.K)
|
||||
fc, rc := make([][]byte, len(words)), make([][]byte, len(words))
|
||||
for i := range words {
|
||||
fc[i], rc[i] = codesOf(fwd[i]), codesOf(rev[i])
|
||||
}
|
||||
|
||||
mk := func() scanFunc {
|
||||
var codes []byte
|
||||
return func(seq *obiseq.BioSequence, emit func(word, pos, errors int, reverse bool)) {
|
||||
raw := seq.Sequence()
|
||||
n := len(raw)
|
||||
if n < p.K {
|
||||
return
|
||||
}
|
||||
codes = codes[:0]
|
||||
for _, b := range raw {
|
||||
codes = append(codes, baseCode[b])
|
||||
}
|
||||
windows := n - p.K + 1
|
||||
if p.Circular {
|
||||
codes = append(codes, codes[:p.K-1]...)
|
||||
windows = n
|
||||
}
|
||||
for pos := range windows {
|
||||
win := codes[pos : pos+p.K]
|
||||
for w := range words {
|
||||
for strand, pat := range [2][]byte{fc[w], rc[w]} {
|
||||
errs := 0
|
||||
for j, b := range pat {
|
||||
if win[j] != b {
|
||||
if errs++; errs > p.MaxErrors {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if errs <= p.MaxErrors {
|
||||
emit(w, pos, errs, strand == 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchCorpus(c, words, mk), nil
|
||||
}
|
||||
Reference in new issue
Block a user