mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-11-09 17:24:41 +00:00
Introduces parallel processing for clustering, refines scoring mechanisms, and expands test coverage for the alignment functionality. This change introduces parallel processing capabilities within the clustering logic, updates the core similarity engine with new data structures and scoring methods, and implements a comprehensive test suite for the alignment tool. It also adds new command-line options for configuring alignment parameters and reference lengths.
107 lines
3.0 KiB
Go
107 lines
3.0 KiB
Go
package obisimilarity
|
|
|
|
import "sort"
|
|
|
|
// Member is a copy of the entry of a sequence in an Index, with its own table.
|
|
type Member struct {
|
|
Entry
|
|
ID int // identifier given by the caller
|
|
Rank int // rank in the order of insertion
|
|
}
|
|
|
|
const tabChunk = 1024
|
|
|
|
// A Bucket holds the members of one length, in the order of insertion, and
|
|
// their tables by blocks (see tableBlock).
|
|
type Bucket struct {
|
|
Members []Member
|
|
blocks []tableBlock
|
|
width int
|
|
}
|
|
|
|
// Commons caches the result of the filter on one block of a bucket.
|
|
type Commons struct {
|
|
v [64]uint8
|
|
blk int
|
|
}
|
|
|
|
// Reset must be called when changing of bucket.
|
|
func (c *Commons) Reset() { c.blk = -1 }
|
|
|
|
// Common returns the sum of the minima of the 4-mer counters of the sequence
|
|
// whose Sparse words are given and of the member k of the bucket (without the
|
|
// overflow term), or -1 if it is not available, in which case PairScore
|
|
// computes it.
|
|
func (b *Bucket) Common(k int, sparse []uint16, c *Commons) int {
|
|
if b.width == 0 || sparse == nil {
|
|
return -1
|
|
}
|
|
if blk := k / b.width; blk != c.blk {
|
|
b.blocks[blk].common(b.width, sparse, c.v[:])
|
|
c.blk = blk
|
|
}
|
|
return int(c.v[k%b.width])
|
|
}
|
|
|
|
// Index stores sequences by length. A sequence can only be similar to the ones
|
|
// whose length is compatible with its own (see Scoring.LengthCompatible), so
|
|
// it only visits the buckets of these lengths, instead of all the sequences,
|
|
// and the filter works on blocks of sequences at once.
|
|
//
|
|
// An Index can grow, but not while it is read.
|
|
type Index struct {
|
|
width int // sequences per block of transposed tables, 0 if unused
|
|
byLen map[int]*Bucket
|
|
lens []int // lengths that have a bucket, increasing
|
|
tabs [][]Table4 // chunks holding the tables of the members
|
|
n int
|
|
}
|
|
|
|
// NewIndex returns an empty index.
|
|
func NewIndex() *Index {
|
|
return &Index{width: simdBlockWidth(), byLen: make(map[int]*Bucket)}
|
|
}
|
|
|
|
// Len is the number of sequences in the index.
|
|
func (x *Index) Len() int { return x.n }
|
|
|
|
// Lengths returns the lengths that have a bucket, in increasing order.
|
|
func (x *Index) Lengths() []int { return x.lens }
|
|
|
|
// Bucket returns the bucket of the sequences of the given length.
|
|
func (x *Index) Bucket(length int) *Bucket { return x.byLen[length] }
|
|
|
|
// Add stores a copy of the entry e as the next member of the index, with the
|
|
// identifier id, and returns it.
|
|
func (x *Index) Add(e Entry, id int) Member {
|
|
if x.n%tabChunk == 0 {
|
|
x.tabs = append(x.tabs, make([]Table4, tabChunk))
|
|
}
|
|
tab := &x.tabs[len(x.tabs)-1][x.n%tabChunk]
|
|
if e.Table != nil {
|
|
*tab = *e.Table
|
|
}
|
|
e.Table = tab
|
|
|
|
b := x.byLen[e.Length]
|
|
if b == nil {
|
|
b = &Bucket{width: x.width}
|
|
x.byLen[e.Length] = b
|
|
x.lens = append(x.lens, e.Length)
|
|
sort.Ints(x.lens)
|
|
}
|
|
if w := x.width; w > 0 && !e.Ambiguous {
|
|
if len(b.Members)%w == 0 {
|
|
b.blocks = append(b.blocks, newTableBlock(w))
|
|
}
|
|
b.blocks[len(b.blocks)-1].set(w, len(b.Members)%w, tab)
|
|
} else if w > 0 && len(b.Members)%w == 0 {
|
|
b.blocks = append(b.blocks, newTableBlock(w))
|
|
}
|
|
|
|
m := Member{Entry: e, ID: id, Rank: x.n}
|
|
b.Members = append(b.Members, m)
|
|
x.n++
|
|
return m
|
|
}
|