Skip to content

Commit 4ca2287

Browse files
authored
merge: Merge pull request #8 from St4NNi/feat/reworkstorage
feat: Feat/reworkstorage
2 parents 5bdda3d + 3ccb9d5 commit 4ca2287

43 files changed

Lines changed: 9889 additions & 5264 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 247 additions & 469 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "jam-rs"
3-
version = "0.9.9"
3+
version = "0.9.10"
44
edition = "2024"
55
repository = "https://github.com/St4NNi/jam-rs"
66
license = "MIT"
@@ -27,23 +27,32 @@ name = "jam_rs"
2727
path = "src/lib.rs"
2828

2929
[dependencies]
30-
anyhow = "1.0.100"
30+
anyhow = "1.0.101"
31+
dashmap = "6.1.0"
3132
jamhash = "0.1.2"
32-
needletail = "0.6.3"
33+
needletail = { version = "0.6.3", features = [
34+
"flate2",
35+
"bzip2",
36+
"xz2",
37+
"zstd",
38+
] }
3339
rayon = "1.11.0"
3440
serde = { version = "1.0.228", features = ["derive"] }
35-
clap = { version = "4.5.54", features = ["derive"] }
36-
itertools = "0.14.0"
37-
heed = "0.22.1-nested-rtxns-6"
38-
byteorder = "1.5.0"
39-
integer-encoding = "4.1.0"
40-
indicatif = { version = "0.18.3", features = ["rayon"] }
41+
serde_json = "1.0"
42+
clap = { version = "4.5.57", features = ["derive"] }
4143
tempfile = "3.24.0"
42-
crossbeam-channel = "0.5.15"
43-
serde_json = "1.0.149"
44+
crossfire = "3.0.4"
45+
bytemuck = { version = "1.25.0", features = ["derive"] }
46+
memmap2 = "0.9"
47+
xorf = { version = "0.12", features = ["serde"] }
48+
thiserror = "2"
49+
indicatif = "0.18.3"
50+
51+
[target.'cfg(not(target_env = "msvc"))'.dependencies]
52+
tikv-jemallocator = "0.6"
4453

4554
[dev-dependencies]
46-
criterion = { version = "0.8.1", features = ["html_reports"] }
55+
criterion = { version = "0.8.2", features = ["html_reports"] }
4756
jamhash = "0.1.2"
4857
murmurhash3 = { version = "0.0.5" }
4958
fastmurmur3 = "0.2.0"

README.md

Lines changed: 98 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,33 @@
66

77
# jam-rs
88

9-
Just another minhash (jam) implementation. A high-performance minhashing tool for genomic sequence similarity analysis, specifically optimized for plasmids and other small genomic elements.
9+
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-
Implements the FracMinHash algorithm for rapid similarity comparisons with enhanced metadata tracking including GC content and sequence length categories tuned for typical plasmid ranges.
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.
1212

1313
### Installation
1414

15-
Install the latest release from [crates.io](https://crates.io/):
15+
From [crates.io](https://crates.io/crates/jam-rs):
1616

1717
```bash
1818
cargo install jam-rs
1919
```
2020

21-
Or install the development version from git:
21+
From source:
2222

2323
```bash
2424
cargo install --git https://github.com/St4NNi/jam-rs
2525
```
2626

2727
### Key Features
2828

29-
- **Plasmid-optimized**: GC content and length categories specifically tuned for plasmid analysis (30-70% GC, 1kB-500kB lengths)
30-
- **Fast sketching**: Entropy-filtered k-mers with optimized hash functions to exclude low-complexity regions
31-
- **Rich metadata**: Enhanced metadata tracking with file index, GC category, and length category for each hash
29+
- **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
31+
- **Complexity filtering**: Shannon entropy threshold to exclude low-complexity k-mers
3232
- **Memory-efficient**: External sorting for processing datasets larger than available RAM
33-
- **LMDB storage**: Fast random access and compact representation with dual database structure
34-
- **Parallel execution**: File-level parallelization with configurable thread count
35-
36-
### Scaling Methods
37-
38-
Multiple scaling methods for different use cases:
39-
- **FracMinHash** (`--fscale`): Restricts hash-space to a fraction of `u64::MAX` / `fscale`
40-
- **Max hashes** (`--nmax`): Limits maximum number of hashes per sequence (memory control)
41-
- **Complexity filtering** (`--complexity`): Only hash sequences with Shannon entropy above threshold (default: 0.0)
42-
- **Singleton mode** (`--singleton`): Creates separate sketch per sequence record
33+
- **Compact storage**: 256-bucket memory-mapped `.jam` format with binary fuse filters for fast random access
34+
- **Parallel execution**: File-level parallelization via rayon with configurable thread count
35+
- **Tuned for speed**: jemalloc allocator, LTO, single codegen unit, `opt-level = 3`
4336

4437
### Usage
4538

@@ -51,21 +44,23 @@ Usage: jam [OPTIONS] <COMMAND>
5144

5245
Commands:
5346
sketch Sketch one or more files and write the result to an output file
54-
dist Estimate containment of a (small) sketch against a subset of one or more sketches as database. Requires all sketches to have the same kmer size
55-
stats Display statistics about an LMDB database
47+
dist Estimate containment of a query sequence against a sketch database
48+
bias Build and analyze hash bias tables for filtering
49+
stats Display statistics about a JAM database
5650
help Print this message or the help of the given subcommand(s)
5751

5852
Options:
5953
-t, --threads <THREADS> Number of threads to use [default: 1]
6054
-f, --force Overwrite output files
61-
-s, --silent Silent mode, no (additional) output to stdout Only errors and output files will be printed
55+
-s, --silent Silent mode, no (additional) output to stdout
56+
-m, --memory <MEMORY> Maximum memory usage in GB [default: 2]
6257
-h, --help Print help
6358
-V, --version Print version
6459
```
6560

6661
#### Sketching
6762

68-
Create sketches from FASTA/FASTQ files. Supports single files, multiple files, or directories.
63+
Create `.jam` databases from FASTA/FASTQ files (plain or gzip/bzip2/xz/zstd compressed). Supports single files, multiple files, or directories.
6964

7065
```console
7166
$ jam sketch --help
@@ -77,104 +72,134 @@ Arguments:
7772
[INPUT]... Input file(s), directories, or file with list of files to be hashed
7873

7974
Options:
80-
-o, --output <OUTPUT> Output file (.lmdb will be appended if not present)
75+
-o, --output <OUTPUT> Output file (.jam format)
8176
-k, --kmer-size <KMER_SIZE> K-mer size, all sketches must have the same size to be compared and below 32 [default: 21]
8277
--fscale <FSCALE> Scale the hash space to a minimum fraction of the maximum hash value (FracMinHash)
83-
--nmax <NMAX> Maximum number of k-mers (per record) to be hashed, top cut-off
84-
--complexity <COMPLEXITY> Complexity cut-off, only hash sequences with complexity above this value This is created via shannon entropy [default: 0.0]
85-
--singleton Create a separate sketch for each sequence record Will increase the size of the output file
86-
-t, --threads <THREADS> Number of threads to use [default: 1]
87-
-f, --force Overwrite output files
78+
--complexity <COMPLEXITY> Complexity cut-off, only hash sequences with complexity above this value [default: 0.0]
79+
--singleton Create a separate sketch for each sequence record
80+
--temp-dir <TEMP_DIR> Custom temporary directory for intermediate files during sorting
81+
--bias-table <BIAS_TABLE> Path to a bias table file (.bias) for compositional filtering
8882
-h, --help Print help
8983
```
9084

9185
Examples:
9286
```bash
93-
# Basic plasmid sketching
94-
jam sketch plasmid.fasta -o sketch.lmdb
87+
# Sketch a single file
88+
jam sketch input.fasta -o sketch.jam
9589

96-
# Multiple plasmid files with custom k-mer size
97-
jam sketch plasmids/ -o plasmid_db.lmdb -k 21 -t 8
90+
# Sketch a directory with 8 threads and FracMinHash scaling
91+
jam sketch genomes/ -o db.jam --fscale 1000 -t 8
9892

99-
# Large collections with memory limits and complexity filtering
100-
jam sketch large_collection/ -o database.lmdb --nmax 10000 --fscale 1000000 --complexity 1.5
93+
# Filter low-complexity k-mers by Shannon entropy
94+
jam sketch genomes/ -o db.jam --fscale 1000 --complexity 1.5
10195

102-
# Separate sketch per plasmid sequence
103-
jam sketch multi_plasmids.fasta -o sketches.lmdb --singleton
96+
# One sketch per sequence record
97+
jam sketch multi.fasta -o db.jam --singleton
98+
99+
# Apply bias filtering during sketching
100+
jam sketch plasmids/ -o filtered.jam --bias-table host_filter.bias
104101
```
105102

106-
#### Distance Calculation
103+
#### Querying
107104

108-
Compare sequences against a sketch database. Supports both raw sequence files and pre-computed sketches.
105+
Estimate containment of query sequences against a sketch database.
109106

110107
```console
111108
$ jam dist --help
112-
Estimate containment of a (small) sketch against a subset of one or more sketches as database. Requires all sketches to have the same kmer size
109+
Estimate containment of a query sequence against a sketch database
113110

114111
Usage: jam dist [OPTIONS] --input <INPUT> --database <DATABASE>
115112

116113
Options:
117-
-i, --input <INPUT> Input sketch or raw sequence file
118-
-d, --database <DATABASE> Database sketch (.lmdb file)
114+
-i, --input <INPUT> Input FASTA/FASTQ file to query
115+
-d, --database <DATABASE> Database sketch (.jam file)
119116
-o, --output <OUTPUT> Output to file instead of stdout
120117
-c, --cutoff <CUTOFF> Cut-off value for similarity/containment [default: 0.0]
121-
--singleton Singleton mode, process each query sequence separately
122-
-t, --threads <THREADS> Number of threads to use [default: 1]
123-
-f, --force Overwrite output files
118+
--singleton Singleton mode, process each query sequence separately
124119
-h, --help Print help
125120
```
126121

127122
Examples:
128123
```bash
129-
# Query plasmid against database
130-
jam dist -i query_plasmid.fasta -d plasmid_db.lmdb -c 0.1 -o results.tsv
131-
132-
# Sketch-to-sketch comparison
133-
jam dist -i query.lmdb -d plasmid_db.lmdb -c 0.05
124+
# Query against a database with a containment cutoff
125+
jam dist -i query.fasta -d db.jam -c 0.1 -o results.tsv
134126

135-
# Process each sequence separately with singleton mode
136-
jam dist -i multi_query.fasta -d plasmid_db.lmdb --singleton -c 0.1
127+
# Per-sequence queries
128+
jam dist -i multi_query.fasta -d db.jam --singleton -c 0.1
137129
```
138130

139-
#### Statistics
131+
Output is tab-separated: `query`, `sample_id`, `hit_count`, `containment`.
140132

141-
Display database statistics including hash counts and distribution analysis.
133+
#### Bias Table Construction
134+
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.
136+
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).
138+
139+
**How it works:**
140+
141+
1. K-mer frequencies from both the positive and negative input sets are counted into separate CMS tables.
142+
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.
143+
3. A log-ratio weight is computed per CMS cell: `log((pos + alpha) / (adjusted_neg + alpha))`, where `alpha` is a smoothing parameter.
144+
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.
142146

143147
```console
144-
$ jam stats --help
145-
Display statistics about an LMDB database
148+
$ jam bias create --help
149+
Create a bias table from positive (target) and negative (background) FASTA files.
150+
Target signal is always subtracted from background before computing bias weights.
146151

147-
Usage: jam stats [OPTIONS] --input <INPUT>
152+
Usage: jam bias create [OPTIONS] --positive <POSITIVE> --negative <NEGATIVE> --output <OUTPUT>
148153

149154
Options:
150-
-i, --input <INPUT> Input LMDB database
151-
-s, --short Short summary only
152-
-t, --threads <THREADS> Number of threads to use [default: 1]
153-
-f, --force Overwrite output files
154-
-h, --help Print help
155+
--positive <POSITIVE> Positive (target) FASTA file(s)
156+
--negative <NEGATIVE> Negative (background) FASTA file(s)
157+
-o, --output <OUTPUT> Output bias table file (.bias)
158+
-k, --kmer-size <KMER_SIZE> K-mer size (must match sketch) [default: 21]
159+
--fscale <FSCALE> FracMinHash scale (must match sketch) [default: 1000]
160+
--cms-width <CMS_WIDTH> CMS columns, power of 2 recommended [default: 1048576]
161+
--cms-depth <CMS_DEPTH> CMS hash functions [default: 5]
162+
--alpha <ALPHA> Smoothing parameter for log-ratio [default: 1.0]
163+
--fold-enrichment <FOLD_ENRICHMENT> Target fold enrichment (auto-maximized if not set)
164+
--threads <THREADS> Number of threads
165+
-h, --help Print help
155166
```
156167

157168
Examples:
158169
```bash
159-
# Summary statistics
160-
jam stats -i plasmid_db.lmdb --short
170+
# Build a bias table to filter out host sequences
171+
jam bias create --positive plasmids.fasta --negative host_genome.fasta -o host_filter.bias
161172

162-
# Detailed distributions
163-
jam stats -i plasmid_db.lmdb
173+
# With custom fold enrichment target
174+
jam bias create --positive targets.fasta --negative background.fasta -o filter.bias --fold-enrichment 10.0
175+
176+
# Inspect a bias table
177+
jam bias stats filter.bias
178+
jam bias stats filter.bias -o report.json
164179
```
165180

166-
### Output Format
181+
#### Statistics
167182

168-
Distance results are tab-separated with columns:
169-
```
170-
query target containment_query_in_target containment_target_in_query jaccard shared_hashes query_hashes target_hashes
171-
```
183+
Display database statistics including hash counts and distribution analysis.
184+
185+
```console
186+
$ jam stats --help
187+
Display statistics about a JAM database
172188

173-
Statistics include hash counts, GC content distribution, and sequence length categories optimized for plasmids and small genomic elements.
189+
Usage: jam stats [OPTIONS] --input <INPUT>
174190

175-
### Algorithm
191+
Options:
192+
-i, --input <INPUT> Input JAM database (.jam file)
193+
--short Short summary only
194+
--full Include the full entry statistics
195+
-h, --help Print help
196+
```
176197

177-
JAM uses entropy-filtered k-mers to exclude low-complexity regions, stores rich metadata (file index, GC category, length category) with each hash, and employs external sorting for memory-efficient processing of large datasets. The categorization system is specifically tuned for plasmid analysis, with fine-grained bins in typical plasmid size and GC content ranges.
198+
Examples:
199+
```bash
200+
jam stats -i db.jam --short
201+
jam stats -i db.jam --full
202+
```
178203

179204
### License
180205

@@ -186,4 +211,4 @@ If you have any ideas, suggestions, or issues, please don't hesitate to open an
186211

187212
### Credits
188213

189-
This tool is inspired by [finch-rs](https://github.com/onecodex/finch-rs) and [sourmash](https://github.com/sourmash-bio/sourmash). Check them out if you need a more mature ecosystem with well tested hash functions and more features.
214+
This tool is inspired by [finch-rs](https://github.com/onecodex/finch-rs) and [sourmash](https://github.com/sourmash-bio/sourmash). Check them out if you need a more mature ecosystem.

benches/benchmarks.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ fn bench_hash_functions(c: &mut Criterion) {
2626
group.warm_up_time(Duration::from_millis(500));
2727
group.measurement_time(Duration::from_secs(2));
2828

29-
// Pre-generate enough values for the benchmark
30-
let mut values = (0..100000u64).into_iter().cycle();
29+
let mut values = (0..100000u64).cycle();
3130

3231
group.bench_function("xxhash3", |b| {
3332
b.iter(|| xxhash3(black_box(values.next().unwrap())))

0 commit comments

Comments
 (0)