jamhash provides a fast, non-cryptographic 64-bit hash for canonical DNA
kmers packed into a u64 with two bits per base. jam-rs uses this mapping in
its reference-guided FracMinHash screening pipeline; this repository evaluates
the hash mapping, not the accuracy or safety of that complete system.
cargo add jamhashThe minimum supported Rust version (MSRV) is 1.86.
use jamhash::jamhash_u64;
// Canonicalization and two-bit packing are caller responsibilities.
let canonical_kmer = 0x0000_006f_93b4_b1bc;
assert_eq!(jamhash_u64(canonical_kmer), 0xaf47_a961_8481_4793);The exact jamhash_u64 formula and compatibility rules are documented in
ALGORITHM.md. A u64 can hold at most 32 two-bit bases. The
checked-in exhaustive evidence is specifically for k=21.
jamhash_u64(0) == 0 is intentional. Callers must define a zero policy; current
jam-rs screening paths exclude zero before applying the FracMinHash threshold.
The archived analysis counted zero inclusively; both conventions are reported
below.
Criterion results at source revision 6c385660e1c115d015eb06a8bfa3aee73a452eb8
on an AMD Ryzen 9 7945HX used rustc 1.97.1 (LLVM 22.1.6),
x86_64-unknown-linux-gnu, opt-level=3, LTO, and one codegen unit:
| Operation | Median | 95% CI | Throughput |
|---|---|---|---|
jamhash_u64 |
2.722 ns | 2.715–2.728 ns | 367.37 million/s |
XXH3 xxh3_64, pre-encoded big-endian u64 |
5.811 ns | 5.807–5.824 ns | 172.07 million/s |
fastmurmur3 murmur3_x64_128, low 64 bits, pre-encoded, seed 42 |
10.575 ns | 10.523–10.609 ns | 94.56 million/s |
All three functions received the same 14 fixed values. Input selection and byte
encoding were outside the measured operation, and inputs and outputs passed
through black_box. These are microbenchmark results for the named machine and
build, not a portable speed multiplier or an end-to-end jam-rs measurement.
Run the benchmark with:
cargo bench --bench benchmarksTo check the hash characteristics and compare it to other hashing functions we did an exhaustive test computing all 2,199,023,255,552 canonical bit-kmers with k >= 21.
Across all 2,199,023,255,552 canonical 21-mers, jamhash_u64 produced
130,928 collision pairs: 99.8901% of the 131,072 pairs expected from a random
64-bit mapping. Every collision was a doublet. The result contains 130,928
duplicate outputs, 261,856 affected inputs, and no multiplicity above two.
| Hash | Collision pairs | Observed / random-map expectation | Affected inputs | Maximum multiplicity |
|---|---|---|---|---|
jamhash_u64 |
130,928 | 0.998901 | 261,856 | 2 |
| fastmurmur3, seed 42 | 139,644 | 1.065399 | 279,288 | 2 |
| XXH3 | 0 | N/A (permutation) | 0 | — |
| Wang 64-bit mixer | 0 | N/A (permutation) | 0 | — |
Zero collisions do not imply more random MinHash sampling. The exact XXH3 path above hashes one fixed eight-byte word, and XXH3 is bijective for eight-byte inputs; the Wang mixer is also a 64-bit permutation. Their zero collision counts are therefore structural. FracMinHash instead needs related kmers to behave like reproducible, nearly independent uniform samples around its hash threshold.
The stored aggregate also records zero-inclusive FracMinHash threshold counts
and output-bit balance for the complete input. For jamhash_u64, the largest
absolute error at the recorded scales 10, 100, 1,000, and 10,000 was
4.728272870180295e-7; the largest recorded output-bit frequency error was
1.0940702850348316e-6. The checked-in verifier recomputes consistency and
collision summaries from this aggregate. The raw multi-terabyte hashset is not
in this checkout, but a surviving sorted partition was available for the
bounded zero-count check.
That posthoc check found exactly 1 zero-valued output from jamhash_u64 across
the complete canonical packed k=21 domain. The archived pipeline used only
hash < u64::MAX / scale, so its original counts include zero. Current
jam-rs uses hash != 0 && hash < u64::MAX / scale; the runtime-equivalent
counts therefore subtract one without altering the archived evidence:
| Scale | Archived, zero-inclusive | Runtime-equivalent, zero excluded |
|---|---|---|
| 10 | 219,901,285,797 | 219,901,285,796 |
| 100 | 21,990,205,024 | 21,990,205,023 |
| 1,000 | 2,199,005,865 | 2,199,005,864 |
| 10,000 | 219,883,231 | 219,883,230 |
The error figures above describe the archived zero-inclusive counts. Measurement
provenance and the derived values are in
zero_policy.json.
A separate, smaller study selected canonical parents satisfying
raw & 0x3f == 0. It measured 4,295,504,166,912 single-base substitutions
from 68,182,605,824 parents; mutated words were not canonicalized again. With
the two-bit encoding above, two of the three possible substitutions at every
base change exactly one packed input bit; the third changes two. The test binned
the high byte of the wrapping hash(parent) - hash(mutant) difference into 256
equal bins:
| Hash | Mutation-difference χ² (lower is closer to uniform) | Mean output bits changed |
|---|---|---|
jamhash_u64 |
218.80 | 31.99999 |
| fastmurmur3, seed 42 | 251.07 | 32.00000 |
| XXH3 | 1,887,897.85 | 32.00000 |
| Wang 64-bit mixer | 3,914,408,417.27 | 32.05177 |
For these packed k=21 mutation pairs, jamhash_u64 was near the
uniform-difference baseline, while XXH3 and Wang retained strong arithmetic
differential structure. XXH3's near-ideal 32-bit mean avalanche shows why
collision counts and average avalanche alone do not answer the MinHash
question. This result supports jamhash_u64 for threshold sampling of related
packed k=21 inputs; it does not by itself establish that XXH3 is unusable.
The mutation observations are dependent. The χ² values are descriptive
comparisons, not p-values or direct measures of FracMinHash screening accuracy.
The aggregate results are in
linear_test.json.
The collision study covered every canonical 21-mer using two-bit A=00,
C=01, G=10, T=11 encoding and
min(forward, reverse_complement) canonicalization. Its archived aggregate is in
combined_metrics.json.
The study used jamhash 0.1.2; version 0.1.3 keeps the identical jamhash_u64
mapping. Exact provenance, immutable source links, artifact hashes, parameters,
known environment details, and missing run metadata are recorded in
test results and reproducibility. Run the modest
posthoc checks with:
python3 scripts/verify_evidence.py- The exhaustive aggregate establishes the reported collision counts, multiplicities, zero-inclusive threshold counts, and stored distribution summaries for all canonical packed k=21 inputs. Runtime-equivalent threshold counts exclude the single measured zero output.
- The separate mutation study supports only its reported comparisons for its selected parent rule and non-canonicalized mutations.
- Neither study measures screening sensitivity, specificity, false-positive or false-negative rates, behavior on labeled biological samples, or deployment safety. Those claims require representative labeled datasets, an evaluation of the complete screening pipeline, and operational validation.
- Adversarial resistance, collision behavior at other kmer sizes, and architecture-independent byte hashing have not been established.
jamhash_bytes and JamHasher support arbitrary bytes and
std::hash::Hasher, but they are not covered by the jamhash_u64 mapping
promise. Their paths for four or more bytes use native-endian reads, so their
outputs are architecture-local and must not be persisted or exchanged between
systems.
JamHasher treats each non-empty write call as a separate frame. Split writes
generally differ from one concatenated write, empty writes are no-ops, and even
a one-write result is not jamhash_bytes. BuildHasherDefault<JamHasher> uses
a fixed zero seed. It can be used for trusted, process-local keys, but not for
network data, uploaded sequence files, shared databases, or other
attacker-controlled keys.
The retained SMHasher3 transcript reports 188/188 passes for a historical JamHash adapter, but its source revision, command, runner version, build flags, and machine were not recorded. It is not evidence that this release was rerun through SMHasher3.
This crate is not cryptographic and provides no adversarial collision or denial-of-service resistance.
jamhash draws inspiration from other high-performance non-cryptographic hash implementations, including folded-multiply designs:
- rapidhash for fast folded multiplication;
- foldhash for efficient folding techniques;
- aHash for high-quality non-cryptographic hashing.
Licensed under either Apache-2.0 or MIT, at your option. See
LICENSE-APACHE and LICENSE-MIT.