Skip to content

Commit dbab302

Browse files
committed
feat: Update Bias handling to use a smoothed look up table (LUT)
1 parent c343275 commit dbab302

6 files changed

Lines changed: 474 additions & 726 deletions

File tree

README.md

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Just another minhash (jam). A high-performance FracMinHash implementation for genomic sequence similarity analysis, optimized for searching plasmids, phages, and other small genomic elements in large datasets.
1010

11-
jam uses a custom hash function ([jamhash](https://github.com/St4NNi/jamhash)) that provides lower collision rates, 2-10x higher speed and better uniformity than murmur3. It also includes a compact memory-mapped database format (`.jam`) for fast random access, and a bias filtering system based on Count-Min Sketches to selectively increase sensitivity for target sequences with either hard cutoffs or soft sigmoid filtering.
11+
jam uses a custom hash function ([jamhash](https://github.com/St4NNi/jamhash)) that provides lower collision rates, 2-10x higher speed and better uniformity than murmur3. It also includes a compact memory-mapped database format (`.jam`) for fast random access, and a bias filtering system based on Count-Min Sketches to selectively increase sensitivity for target sequences with either hard cutoffs or a enrichment-based look up table (LUT) filtering.
1212

1313
### Installation
1414

@@ -24,10 +24,12 @@ From source:
2424
cargo install --git https://github.com/St4NNi/jam-rs
2525
```
2626

27+
Conda, Docker images and python bindings are planned for the future. In the meantime, you can use the CLI tool directly or call the Rust library from your own Rust code.
28+
2729
### Key Features
2830

2931
- **Custom hash function**: [jamhash](https://github.com/St4NNi/jamhash) provides lower collisions, better uniformity and is faster compared to murmur3
30-
- **Bias-aware sketching**: Count-Min Sketch based compositional filtering with automatic background extraction and optional soft sigmoid filtering
32+
- **Bias-aware sketching**: Count-Min Sketch based compositional filtering with automatic background extraction and optional per-bucket enrichment LUT filtering
3133
- **Complexity filtering**: Shannon entropy threshold to exclude low-complexity k-mers
3234
- **Memory-efficient**: External sorting for processing datasets larger than available RAM
3335
- **Compact storage**: 256-bucket memory-mapped `.jam` format with binary fuse filters for fast random access
@@ -132,63 +134,49 @@ Output is tab-separated: `query`, `sample_id`, `hit_count`, `containment`.
132134

133135
#### Bias Table Construction
134136

135-
Bias tables allow compositional filtering to increase sensitivity for target sequences while suppressing background noise. They work by scoring k-mers based on their enrichment in a positive (target) set relative to a negative (background) set. By default, hashes are filtered with a hard threshold, but you can enable soft sigmoid filtering by supplying both `--positive-fscale` and `--negative-fscale` to smoothly vary retention by bias weight.
137+
Bias tables allow compositional filtering to increase sensitivity for target sequences while suppressing background noise. They work by scoring k-mers based on their enrichment in a positive (target) set relative to a negative (background) set. By default, hashes are filtered with a hard threshold, but you can enable per-bucket enrichment LUT filtering by supplying both `--positive-fscale` and `--negative-fscale`.
136138

137-
The underlying data structure is a **Count-Min Sketch (CMS)**, a probabilistic structure that approximates k-mer frequencies using multiple independent hash functions mapped to a fixed-width table. This keeps memory usage constant regardless of the number of distinct k-mers. By default, the CMS uses 1,048,576 columns and 5 hash functions (~5 MB).
139+
The underlying data structure is a **Count-Min Sketch (CMS)**, a probabilistic structure that approximates k-mer frequencies using multiple independent hash functions mapped to a fixed-width table. This keeps memory usage constant regardless of the number of distinct k-mers. By default, the CMS uses 1,048,576 columns and 5 hash functions (~5 MB), this should be increased if the expected number of distinct k-mers exceeds ~100 million.
138140

139141
**How it works:**
140142

141143
1. K-mer frequencies from both the positive and negative input sets are counted into separate CMS tables.
142144
2. **Background extraction**: The positive counts are subtracted from the negative counts (floored at zero). This prevents k-mers naturally shared between target and background from being penalized.
143145
3. A log-ratio weight is computed per CMS cell: `log((pos + alpha) / (adjusted_neg + alpha))`, where `alpha` is a smoothing parameter.
144146
4. Weights are quantized to `i8` (-127 to +127) for compact storage.
145-
5. **Threshold calibration**: All 255 possible thresholds are evaluated. The threshold that maximizes fold enrichment (positive retention / negative retention) is selected. If a target fold enrichment is specified, the closest achievable threshold is used instead.
146-
6. **Soft filtering (optional)**: When `--positive-fscale` and `--negative-fscale` are set, the threshold defines the midpoint of a sigmoid that maps bias weights to an effective fscale. Higher weights retain hashes near `positive-fscale`, lower weights drift toward `negative-fscale` (or are dropped entirely if `negative-fscale` is `drop`).
147-
148-
```console
149-
$ jam bias create --help
150-
Create a bias table from positive (target) and negative (background) FASTA files.
151-
Target signal is always subtracted from background before computing bias weights.
147+
5. **Threshold calibration**: All 255 possible thresholds are evaluated. The threshold that maximizes fold enrichment (positive retention / negative retention) is selected.
148+
6. **Enrichment LUT (optional)**: When `--positive-fscale` and `--negative-fscale` are set, each weight bucket independently gets an effective fscale derived from its empirical enrichment ratio (positive/negative hash frequency). The optimizer maximizes `pos_retention² / neg_retention` subject to a minimum positive retention floor (`--min-positive-retention`). The resulting response curve directly reflects the biological data, with no monotonicity or smoothness constraints imposed. Buckets with insufficient observations inherit from the nearest reliable neighbor.
149+
7. **Unbiased fscale (optional)**: `--unbiased-fscale` sets a fixed effective fscale for weight-zero buckets (k-mers with equal positive and negative frequency), independent of the LUT optimizer.
152150

153-
Usage: jam bias create [OPTIONS] --positive <POSITIVE> --negative <NEGATIVE> --output <OUTPUT>
151+
**Effective fscale**: Because the LUT assigns different sampling rates per weight bucket, the overall sampling rate is not uniform. The calibration output reports three derived values:
152+
- `eff. fscale (pos)`: effective fscale on the positive calibration population (`base_fscale / positive_retention`)
153+
- `eff. fscale (neg)`: effective fscale on the negative calibration population (`base_fscale / negative_retention`)
154+
- `eff. fscale (combined)`: geometric mean of the two, useful as a single summary (`base_fscale / sqrt(pos_ret × neg_ret)`)
154155

155-
Options:
156-
--positive <POSITIVE> Positive (target) FASTA file(s)
157-
--negative <NEGATIVE> Negative (background) FASTA file(s)
158-
-o, --output <OUTPUT> Output bias table file (.bias)
159-
-k, --kmer-size <KMER_SIZE> K-mer size (must match sketch) [default: 21]
160-
--fscale <FSCALE> FracMinHash scale (must match sketch) [default: 1000]
161-
--cms-width <CMS_WIDTH> CMS columns, power of 2 recommended [default: 1048576]
162-
--cms-depth <CMS_DEPTH> CMS hash functions [default: 5]
163-
--alpha <ALPHA> Smoothing parameter for log-ratio [default: 1.0]
164-
--fold-enrichment <FOLD_ENRICHMENT> Target fold enrichment (auto-maximized if not set)
165-
--positive-fscale <POSITIVE_FSCALE> Effective fscale for positively biased hashes (soft filter)
166-
--negative-fscale <NEGATIVE_FSCALE> Effective fscale for negatively biased hashes, or "drop"
167-
--steepness <STEEPNESS> Sigmoid steepness (auto-derived if not set)
168-
--threads <THREADS> Number of threads
169-
-h, --help Print help
170-
```
156+
`jam stats` on a database with an embedded bias table also reports the combined effective fscale inline with the sample rate, e.g. `Sample rate: 1/100 (effective: 1/432)`.
171157

172158
Examples:
173159
```bash
174160
# Build a bias table to filter out host sequences
175161
jam bias create --positive plasmids.fasta --negative host_genome.fasta -o host_filter.bias
176162

177-
# With custom fold enrichment target
178-
jam bias create --positive targets.fasta --negative background.fasta -o filter.bias --fold-enrichment 10.0
163+
# Enrichment LUT filtering: base fscale 100, pass plasmid-enriched kmers more, suppress host kmers
164+
jam bias create --positive plasmids.fasta --negative host.fasta -o filter.bias \
165+
--positive-fscale 100 --negative-fscale 10000
179166

180-
# Soft sigmoid filtering with different retention for positive vs negative bias weights
181-
jam bias create --positive targets.fasta --negative background.fasta -o filter.bias \
182-
--positive-fscale 1000 --negative-fscale 5000
167+
# With a stricter positive retention floor and fixed sampling for unbiased kmers
168+
jam bias create --positive plasmids.fasta --negative host.fasta -o filter.bias \
169+
--positive-fscale 100 --negative-fscale 10000 \
170+
--min-positive-retention 0.1 --unbiased-fscale 1000
183171

184-
# Inspect a bias table
172+
# Inspect bias table (text or JSON)
185173
jam bias stats filter.bias
186174
jam bias stats filter.bias -o report.json
187175
```
188176

189177
#### Statistics
190178

191-
Display database statistics including hash counts and distribution analysis.
179+
Display database statistics including hash counts, distribution analysis, and effective fscale when a bias table is embedded.
192180

193181
```console
194182
$ jam stats --help

0 commit comments

Comments
 (0)