43 lines
2.9 KiB
Markdown
43 lines
2.9 KiB
Markdown
# Minimizer selection
|
||||
|
|
|
|||
|
|
## Definition
|
|||
|
|
|
|||
|
|
A **minimizer** of a kmer window is the m-mer ($m < k$) that is smallest, among all $k - m + 1$ overlapping m-mers in the window, under a chosen ordering. The minimizer is always taken in canonical form (lexicographic minimum of forward and reverse complement) so that selection is strand-independent.
|
|||
|
|
|
|||
|
|
The minimizer partitions a sequence into super-kmers: maximal runs of overlapping kmers that share the same minimizer (see [Kmers and super-kmers](kmers_and_superkmers.md)).
|
|||
|
|
|
|||
|
|
## Hash-based ("random") minimizer
|
|||
|
|
|
|||
|
|
`obikmer` selects minimizers by hash order rather than plain lexicographic order. Ordering m-mers lexicographically on their 2-bit encoding systematically favors AT-rich m-mers (an all-A m-mer always encodes to 0), which causes low-complexity regions to dominate as minimizers and produces unbalanced partitions.
|
|||
|
|
|
|||
|
|
Instead, a well-distributed hash function $H$ is applied to the canonical (lexicographically minimal) form of each m-mer, and the m-mer with the smallest $H$ value wins. Because $H$ is a bijection with good avalanche properties, every distinct m-mer in a window has an equal chance of holding the minimum hash value, independent of its nucleotide composition.
|
|||
|
|
|
|||
|
|
The canonical form used as input to $H$ is still the lexicographic minimum of forward/reverse-complement — hashing is applied on top of it, not used to redefine it. Defining canonicity by hash value instead would bias the *distribution of hash values themselves* toward small values (the minimum of two independent hashes is not uniformly distributed), reintroducing a bias one layer down.
|
|||
|
|
|
|||
|
|
### Hash function
|
|||
|
|
|
|||
|
|
The hash function is a 64-bit mixing function (splitmix64-style finalizer) applied to the m-mer XORed with a fixed non-zero seed:
|
|||
|
|
|
|||
|
|
$$H(x) = \text{mix64}(x \oplus s), \quad s = \lfloor 2^{64}/\varphi \rfloor = \texttt{0x9e3779b97f4a7c15}$$
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
H(x):
|
|||
|
|
x ← x ⊕ 0x9e3779b97f4a7c15
|
|||
|
|
x ← x ⊕ (x >> 30)
|
|||
|
|
x ← x × 0xbf58476d1ce4e5b9
|
|||
|
|
x ← x ⊕ (x >> 27)
|
|||
|
|
x ← x × 0x94d049bb133111eb
|
|||
|
|
return x ⊕ (x >> 31)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The XOR seed avoids the finalizer's fixed point at 0 ($\text{mix64}(0) = 0$), which would otherwise make an all-A m-mer (canonical value 0) win every window comparison.
|
|||
|
|
|
|||
|
|
## Partition routing is independent of minimizer selection
|
|||
|
|
|
|||
|
|
The hash used to select a minimizer within a window (the minimum of several hash values) and the hash used to route a super-kmer to a storage partition are computed separately:
|
|||
|
|
|
|||
|
|
- **Selection** uses $H$ applied to every candidate m-mer in the window, keeping the minimum.
|
|||
|
|
- **Partition routing** recomputes $H$ on the single selected minimizer only, once its position is fixed. This is a hash of one specific value, not the minimum of several, so it is uniformly distributed and safe to use directly for routing.
|
|||
|
|
|
|||
|
|
See [Partitioning and indexing architecture](indexing_architecture.md) for how the routing value is turned into a partition index.
|