sketchy
Placement of DNA sequence on a reference genome by k-mer composition, with a calibrated posterior, and an exact-truth benchmark of read placement in human centromeres.
Summary
sketchy places a genomic fragment (a read, a contig, a haplotype) on a reference genome
from its k-mer composition and reports a posterior probability for the call. The project set
out to test whether composition can place sequence in the regions where seed-chain-extend
alignment has traditionally been considered unreliable: alpha-satellite higher-order-repeat
(HOR) arrays, human satellite (HSat) blocks, segmental duplications and the acrocentric short
arms. Version 0.2 is a rewrite of the method and of its evaluation.
The principal result concerns the premise rather than the method. On reads whose origin is known exactly, cut from the read's own complete diploid genome (T2T HG002 v1.1) and given realistic errors, minimap2 and Winnowmap2 place 95-100% of reads correctly in every satellite class. Composition reaches 79% overall and 95% in active HOR arrays, at about one fifth of the wall time and one sixth of the memory. When the reference is a different haplotype or a different individual, array-level placement by the two approaches is indistinguishable, and both fail on the same classes. Composition holds the order of consecutive pieces of a centromere better than alignment in one of two truth-free walks.
A further test places assembly-scale segments, 100 kb tiles of the HG002 v1.1 acrocentric short arms, on CHM13 (§4.11). Every method, alignment or composition, puts 55% of them on their own chromosome, because the closest CHM13 arm is a heterologous one for 44% of the tiles: the short arms exchange sequence between chromosomes (Guarracino et al. 2023), and the chromosome identity of a short-arm segment is a property of the whole chromosome, not of the segment. On the sample's own assembly the same tiles are resolved to 98%.
The exact-truth benchmark (sketchy simulate, bench/cenbench.sh) is released as a
resource: it evaluates any mapper on complete diploid centromeres, by annotation class,
with confidence intervals, haplotype-blind scoring through a whole-genome liftover, and
measured cost. Full methods: docs/METHODS.md. Review of v0.1 and the
reasons for the rewrite: docs/REVIEW.md. Every table below is generated
from the benchmark output files by bench/fill_tables.py.
1. Background
Alignment-free placement of sequence within one genome by sketch composition was introduced by MashMap (Jain et al. 2018; MashMap3, Kille et al. 2023). Rare k-mers have been used as anchors inside satellite arrays by centroFlye, TandemMapper and VerityMap (Bzikadze, Mikheenko and Pevzner 2020-2022) and by ascairn (Shiraishi et al. 2025), and Winnowmap2 (Jain et al. 2022) is the reference method for mapping into repeats. Complete human centromere sequences (Logsdon et al. 2024; the 2,110-centromere survey published in 2026) show that 45.8% of centromeric sequence is not reliably alignable between haplotypes, that HOR arrays mutate 16.6 times faster than their flanks, and that a chromosome may carry any of hundreds of centromere haplotypes. These findings bound what any placement method can achieve across haplotypes; they say nothing about placement on the sample's own assembly.
Version 0.1 of this repository (preserved at git show dec7410:README.md) evaluated
composition against alignment only across haplotypes (HPRC assemblies on CHM13), with truth
taken from minimap2, and inferred from those tests that alignment fails in satellites. The
exact-truth benchmark introduced here separates the two questions.
2. Method
The method has four parts; each is stated in full, with every rejected alternative and its measurement, in docs/METHODS.md.
- Reference index. The reference is cut into 10 kb windows. A window keeps all of its distinct 21-mers when it has fewer than a crossover (5,054 on CHM13; an alpha-satellite HOR window has about 2,300) and 256 otherwise, selected by hash. Multi-copy windows with large vocabularies (monomeric and beta satellite, diverged HORs, segmental duplications) are re-sketched at four times the budget in a second pass. Every kept k-mer carries every position at which it occurs in the window. The index is a single memory-mapped file.
- Coarse stage. Every k-mer of the query is looked up; the query is not sketched. A
window's evidence is the sum, over non-overlapping segments of shared k-mers along the
query, of
ln(N/df)for the rarest word of each segment, wheredfis the number of windows carrying the word. Tiles of the query are combined by shift-and-sum on a phase-corrected profile, and windows that share nothing are excluded. - Fine stage. Inside each candidate region, every query k-mer votes for the diagonal of
every stored occurrence of that word, weighted by
ln(N/df), in 20 bp bins smoothed over a width that scales with the excursion of the diagonal expected under the read's own indel rate (set against real reads, whose indels are clustered; section 6). The peak gives strand and base-level position. - Posterior. Candidate regions from the coarse profile are re-weighted by their fine
evidence. Two temperatures, fitted on exact-truth reads and scaled with read length,
convert evidence into probability; a third, smaller temperature makes the decision. The
reported
postis the posterior mass of the call andmapqits Phred form.
3. Benchmark design
Exact-truth reads. sketchy simulate cuts reads from the T2T HG002 v1.1 assembly
(T2T Consortium 2024; 6.0 Gbp, both haplotypes complete through every centromere) and applies a
stated error model, recording each read's origin in its header. Reads are drawn per class
of the assembly's cenSat v2.0 annotation (200 per class) and from outside every annotated
interval (1,000), from either strand. Error presets: ONT R10-like (1.5%, 65% indels,
homopolymer-biased), ONT R9-like (7%), HiFi-like (0.3%) and assembly-like (0.2%). The model
is simple by design and does not reproduce the context dependence of nanopore errors; the
real-read tests exist for that reason.
Scoring. A call is correct when it lands on the right sequence within 10 kb of the
truth. By default the right haplotype is required; haplotype-blind scoring lifts the truth
to the other haplotype through a whole-genome minimap2 -x asm5 alignment of the maternal to
the paternal assembly (unique sequence only; satellite arrays have no cross-haplotype base
correspondence). Per-class accuracies carry 95% Wilson intervals; paired comparisons between
methods use an exact McNemar test on the same reads (bench/paired_test.py).
Competitors. minimap2 2.30 (map-ont, map-hifi, asm20, asm5, asm10),
Winnowmap2 2.03 (map-ont, asm20, with meryl repetitive k-mer lists built per
reference) and MashMap3 3.1.3 (-s 5000 --pi 85 on reads, -s 10000 on the walks), each
with its published defaults otherwise, each run with 5 threads on the same machine (Apple
M1 Max, 64 GB). Wall time
and peak resident memory are recorded for every run.
Other evaluations. (i) Cross-haplotype: reads from the maternal haplotype placed on an index of the paternal one, scored at the array level (right chromosome and same annotation class); (ii) cross-individual: the same reads placed on T2T-CHM13; (iii) 6,000 real HG002 ONT R10.4.1 ultra-long reads placed on the diploid assembly, scored by agreement between methods; (iv) v0.1's benchmarks, HPRC assembly fragments and R9 reads placed on CHM13 with minimap2 as truth; (v) two truth-free walks, in which a contig spanning a centromere is cut into consecutive 50 kb pieces that are placed independently and scored by Kendall's tau-a on the order of the placements.
4. Results
4.1 Exact-truth placement on the read's own genome
20 kb reads, ONT R10-like error, placed on the diploid assembly; fraction within 10 kb on the right haplotype.
| region class | n | sketchy (95% CI) | MashMap3 | minimap2 map-ont |
Winnowmap2 map-ont |
|---|---|---|---|---|---|
| unique | 1000 | 0.780 (0.75-0.81) | 0.891 | 0.973 | 0.975 |
| centromere transition (ct) | 200 | 0.765 (0.70-0.82) | 0.860 | 0.970 | 0.975 |
| monomeric alpha-satellite (mon) | 200 | 0.740 (0.68-0.80) | 0.845 | 0.985 | 0.980 |
| diverged HOR (dhor) | 200 | 0.890 (0.84-0.93) | 0.920 | 0.995 | 0.995 |
| inactive HOR (hor) | 200 | 0.975 (0.94-0.99) | 0.745 | 0.990 | 0.990 |
| active HOR (kinetochore) | 200 | 0.950 (0.91-0.97) | 0.530 | 0.990 | 0.995 |
| mixed alpha | 200 | 1.000 (0.98-1.00) | 0.410 | 1.000 | 1.000 |
| HSat1A | 200 | 0.810 (0.75-0.86) | 0.670 | 0.950 | 0.975 |
| HSat1B | 200 | 0.990 (0.96-1.00) | 0.695 | 1.000 | 1.000 |
| HSat2 | 200 | 0.475 (0.41-0.54) | 0.570 | 1.000 | 1.000 |
| HSat3 | 200 | 0.825 (0.77-0.87) | 0.760 | 1.000 | 0.990 |
| beta satellite (bSat) | 200 | 0.870 (0.82-0.91) | 0.865 | 0.945 | 0.930 |
| gamma satellite (gSat) | 200 | 0.715 (0.65-0.77) | 0.860 | 0.950 | 0.950 |
| other censat | 200 | 0.890 (0.84-0.93) | 0.570 | 0.950 | 0.975 |
| rDNA | 200 | 0.255 (0.20-0.32) | 0.270 | 0.955 | 0.960 |
| all | 3800 | 0.792 (0.78-0.81) | 0.738 | 0.976 | 0.978 |

Alignment resolves every satellite class. minimap2 and Winnowmap2 place 99-100% of reads from active HOR arrays, HSat2 and HSat3 and 96% of rDNA reads, to the base. Composition is the more accurate of the two sketch-based methods in alpha-satellite (active HOR 0.95 against 0.53 for MashMap3, inactive HOR 0.98 against 0.75; exact McNemar p < 10⁻¹² for both) and the less accurate in unique, monomeric and gamma-satellite sequence. Its errors are not imprecision: of the reads it places on the right haplotype and within 10 kb, 99.9% are within 1 kb, and the remainder are placed in the wrong copy of a repeat or on the wrong haplotype, megabases away (figure below). HSat2 and rDNA are the classes in which copies are too alike for a bag of words to separate.

4.2 Other read types
Fraction within 10 kb, right haplotype required.
| read set | n | sketchy | minimap2 map-ont |
Winnowmap2 map-ont |
minimap2 map-hifi |
|---|---|---|---|---|---|
| 20 kb, ONT R9-like (7% error) | 3800 | 0.739 | 0.967 | 0.967 | -- |
| 5 kb, ONT R10-like | 3800 | 0.668 | 0.902 | 0.903 | -- |
| 100 kb, ONT R10-like | 2280 | 0.882 | 0.992 | 0.994 | -- |
| 20 kb, HiFi-like (0.3%) | 3800 | 0.791 | -- | -- | 0.983 |
Composition loses five points between 1.5% and 7% error where the aligners lose one, gains
with read length, and loses most on 5 kb reads, which span a single window. On HiFi-like
reads minimap2 map-hifi reaches 98.3%.
4.3 Placement and phasing
The two haplotypes of HG002 differ by about 0.1% in unique sequence. A 20 kb read carries about 20 heterozygous sites, and a method that samples 2.6% of the k-mers of unique sequence will usually not see the ones that phase it. Haplotype-blind scoring separates the two questions:
| 20 kb ONT R10, within 10 kb | sketchy | MashMap3 | minimap2 map-ont |
Winnowmap2 map-ont |
|---|---|---|---|---|
| right haplotype required | 0.792 | 0.738 | 0.976 | 0.978 |
| either haplotype | 0.867 | 0.783 | 0.984 | 0.986 |
| unique sequence, either haplotype | 0.984 | 0.988 | 0.998 | 0.997 |
| active HOR, either haplotype | 0.950 | 0.535 | 0.990 | 0.995 |
| monomeric, either haplotype | 0.820 | 0.880 | 0.990 | 0.990 |
Composition places 98% of unique reads at the right locus on one haplotype or the other; its deficit there is phasing. The deficits in active HOR, HSat2 and rDNA are unchanged under haplotype-blind scoring and are placement errors. Monomeric alpha-satellite is intermediate: a third of its deficit is phasing, because monomeric arrays are diverged enough to be sketched as sparsely as unique sequence.
4.4 Calibration
Expected calibration error (10 bins) of the reported posterior against correctness within 10 kb; for the aligners, mapq is read as the probability it encodes.
| set | sketchy post |
minimap2 mapq as probability | Winnowmap2 mapq as probability |
|---|---|---|---|
| 20 kb ONT R10 (the fit set) | 0.110 | 0.119 | 0.074 |
| 20 kb HiFi (held out) | 0.096 | -- | -- |
| 100 kb ONT R10 (held out) | 0.107 | 0.052 | 0.011 |
| 20 kb ONT R9 (held out) | 0.176 | 0.190 | 0.142 |
| 5 kb ONT R10 (held out) | 0.140 | 0.182 | 0.246 |

The temperatures fitted on the 20 kb R10 set transfer to HiFi reads and to 100 kb reads, and
scaling them with read length carries the calibration to 5 kb reads. On 20 kb reads
composition's calibration error is comparable to minimap2's mapq read as a probability and
larger than Winnowmap2's; on 5 kb reads it is smaller than either aligner's, and on 100 kb
reads larger than both. The shapes differ: on a diploid reference the aligners' mapq is
under-confident (minimap2 assigns mapq below 10 to 30% of unique 20 kb reads and places
91% of those correctly), whereas composition's errors carry the same kind of evidence as
its correct calls. Consequently a posterior threshold does not isolate a near-perfect
subset: post ≥ 0.99 retains 38% of reads at 88% accuracy, whereas mapq ≥ 20 retains 61%
at 100%.
4.5 Across haplotypes and across individuals
20 kb ONT R10-like reads from HG002's maternal haplotype placed (a) on an index of the paternal haplotype and (b) on T2T-CHM13, scored at the array level: right chromosome, and inside an interval of the same annotation class as the read's origin. In unique sequence the position is additionally checked against minimap2's confident (mapq ≥ 30) placement of the same read.
(a) Maternal reads on the paternal haplotype:
| class | n | sketchy: right array | minimap2: right array |
|---|---|---|---|
| bsat | 150 | 0.613 | 0.573 |
| censat | 150 | 0.453 | 0.480 |
| ct | 150 | 0.820 | 0.827 |
| dhor | 150 | 1.000 | 1.000 |
| gsat | 150 | 0.953 | 0.953 |
| hor | 300 | 0.933 | 0.953 |
| hsat1A | 150 | 0.327 | 0.540 |
| hsat1B | 150 | 0.873 | 0.873 |
| hsat2 | 150 | 0.993 | 1.000 |
| hsat3 | 150 | 0.947 | 0.940 |
| mon | 300 | 0.793 | 0.757 |
| rDNA | 150 | 0.133 | 0.253 |
| unique | 800 | 0.936 (position agreement 0.997) | 0.938 |
| ALL | 2900 | 0.805 | 0.820 |
(b) Maternal reads on CHM13 (a different individual; classes named as in CHM13's annotation):
| class | n | sketchy: right array | minimap2: right array |
|---|---|---|---|
| bsat | 150 | 0.340 | 0.313 |
| censat | 150 | 0.313 | 0.333 |
| ct | 150 | 0.753 | 0.767 |
| dhor | 150 | 0.980 | 0.980 |
| gsat | 150 | 0.953 | 0.953 |
| hor | 300 | 0.947 | 0.947 |
| hsat1A | 150 | 0.493 | 0.500 |
| hsat1B | 150 | 0.933 | 0.933 |
| hsat2 | 150 | 1.000 | 1.000 |
| hsat3 | 150 | 0.933 | 0.933 |
| mon | 300 | 0.427 | 0.423 |
| rDNA | 150 | 0.287 | 0.153 |
| unique | 800 | 0.940 (position agreement 0.999) | 0.940 |
| ALL | 2900 | 0.763 | 0.756 |
Across haplotypes the two methods are within two points of each other overall and fail on the same classes (HSat1A, beta satellite, other censat, rDNA). Across individuals both fall further and monomeric alpha-satellite joins the failing classes: 43% of monomeric reads land in a monomeric interval of the homologous CHM13 chromosome by either method. Reference distance, not the choice of method, determines array-level placement.
4.6 Real ultra-long reads
6,000 HG002 ONT R10.4.1 ultra-long reads (T2T consortium data; median 71 kb, 547 Mbp) placed on the diploid assembly by sketchy and by minimap2. No truth exists for real reads; the table reports agreement (same sequence, start within 10 kb) by the annotation class at the aligner's position.
| class (at aligner position) | n | agreement | agreement when sketchy post ≥ 0.9 (fraction kept) |
agreement when minimap2 mapq ≥ 30 (fraction kept) |
|---|---|---|---|---|
| unique | 5458 | 0.778 | 0.754 (26%) | 0.901 (43%) |
| ct | 159 | 0.799 | 0.914 (44%) | 0.932 (46%) |
| active_hor | 95 | 0.979 | 1.000 (84%) | 0.981 (55%) |
| HSat3 | 80 | 0.825 | 0.933 (75%) | 0.938 (60%) |
| HSat1A | 41 | 0.976 | 0.976 (100%) | 0.973 (90%) |
| HSat1B | 39 | 0.949 | 0.969 (82%) | 1.000 (59%) |
| HSat2 | 35 | 0.829 | 0.966 (83%) | 0.889 (51%) |
| rDNA | 24 | 0.333 | 0.429 (58%) | 0.714 (29%) |
| mon | 21 | 0.905 | 1.000 (48%) | 1.000 (48%) |
| ALL | 5994 | 0.784 | 0.793 (30%) | 0.906 (44%) |
The methods agree on 98% of reads inside active HOR arrays and disagree most in unique sequence, where the disagreement is haplotype choice (minimap2 assigns mapq below 30 to 57% of these reads, and sketchy's posterior is below 0.9 for 74%). Filtering on either tool's confidence raises agreement; in active HOR arrays sketchy's posterior retains 84% of reads at 100% agreement where minimap2's mapq ≥ 30 retains 55% at 98%.
4.7 v0.1's cross-haplotype benchmarks on CHM13
Real HPRC assembly fragments (5-100 kb) and real R9 ultra-long reads placed on CHM13, with truth from minimap2 run offline. Inside satellites this scores agreement with an aligner's arbitrary choice among near-identical candidates, as v0.1 noted; fraction within 100 kb.
| set | n | v0.2 within 100 kb | v0.1 | v0.2 within 1 kb | alpha-satellite HOR | HSat2 | HSat3 |
|---|---|---|---|---|---|---|---|
| HG002 fragments (5-100 kb) | 2100 | 0.944 | 0.941 | 0.905 | 0.539 | 0.289 | 0.574 |
| HG02723 fragments (never tuned on) | 1500 | 0.937 | 0.935 | 0.893 | 0.486 | 0.200 | 0.640 |
| HG002 ONT R9 ultra-long reads (~92% identity) | 1176 | 0.940 | 0.956 | 0.521 | 0.519 | 0.125 | 0.462 |
| satellite-enriched HG002 fragments | 2400 | 0.805 | -- | 0.655 | 0.541 | 0.176 | 0.625 |
| satellite-enriched HG02723 fragments | 2400 | 0.756 | -- | 0.591 | 0.521 | 0.130 | 0.599 |
v0.2 matches v0.1 on the assembly fragments, is 1.5 points lower on the R9 reads, is better outside the satellites and in monomeric alpha-satellite, and places to the base rather than to a window (v0.1's median error was 2.2 kb; 90% of v0.2's fragment placements are within 1 kb). It is worse than v0.1 in HSat2 and HSat3 against minimap2's pick.
4.8 Truth-free walks
Kendall's tau-a on the order of independently placed consecutive 50 kb pieces (1 = perfect order; a method that places every piece at one locus scores 0).
(a) A real HG002 contig spanning the chr1 centromere, placed on CHM13 (v0.1's walk; the aligners' outputs are v0.1's, sketchy is re-run):
| region | pieces | sketchy v0.2 | MashMap3 | best minimap2 | best Winnowmap2 |
|---|---|---|---|---|---|
| unique flank (ct) | 21 | 0.848 | 1.000 | 1.000 (asm5) |
1.000 (map-ont) |
| alpha-satellite HOR array | 142 | 0.740 | 0.592 | 0.484 (map-hifi) |
0.359 (asm20) |
| HSat2 block | 220 | 0.008 | 0.001 | 0.061 (asm20) |
0.033 (map-ont) |
| whole walk | 383 | 0.622 | 0.609 | 0.582 (asm5) |
0.584 (asm20) |
| method | sketchy minus method, tau-a in the HOR array | paired block-bootstrap p |
|---|---|---|
| minimap2 asm20 | +0.381 | < 0.002 |
| minimap2 asm20 -N50 | +0.330 | < 0.002 |
| minimap2 asm10 | +0.387 | < 0.002 |
| minimap2 asm5 | +0.310 | < 0.002 |
| minimap2 map-hifi | +0.256 | < 0.002 |
| MashMap3 | +0.147 | 0.036 |
| Winnowmap2 asm20 | +0.381 | < 0.002 |
| Winnowmap2 map-ont | +0.460 | < 0.002 |

(b) The maternal chr1 centromere of HG002 (120.0-138.5 Mb, 370 pieces) placed on the paternal haplotype:
| region | pieces | sketchy | minimap2 asm20 |
minimap2 map-hifi |
MashMap3 |
|---|---|---|---|---|---|
| unique flank (ct) | 41 | 1.000 | 0.980 | 0.980 | 0.966 |
| alpha-satellite HOR array | 79 | 0.806 | 0.807 | 0.757 | 0.803 |
| HSat2 block | 160 | 0.010 | 0.042 | 0.036 | 0.019 |
| whole walk | 370 | 0.583 | 0.625 | 0.628 | 0.611 |
Composition holds the order of pieces inside the HOR array better than every aligner and
than MashMap3 on the CHM13 walk (tau-a 0.74 against at most 0.59; every margin significant
on a paired moving-block bootstrap, MashMap3 at p = 0.04), and ties
with minimap2 and MashMap3 on the walk between HG002's own haplotypes (sketchy against
minimap2 asm20: −0.001, p = 0.79; against MashMap3: +0.003, p = 0.74). The two walks
differ in reference distance (CHM13 is a different individual; the paternal haplotype is
the same individual) and in contig, and the present data cannot separate the two
explanations. In HSat2 no method orders the pieces.
4.9 Sensitivity to k and window size
Fraction within 10 kb on the 20 kb ONT R10 set, one parameter changed at a time.
| class | k=21, W=10 kb | k=17 | k=25 | k=31 | W=5 kb | W=20 kb |
|---|---|---|---|---|---|---|
| unique | 0.780 | 0.806 | 0.797 | 0.828 | 0.799 | 0.694 |
| centromere transition (ct) | 0.765 | 0.780 | 0.775 | 0.820 | 0.775 | 0.760 |
| monomeric alpha-satellite (mon) | 0.740 | 0.700 | 0.705 | 0.735 | 0.795 | 0.685 |
| diverged HOR (dhor) | 0.890 | 0.865 | 0.690 | 0.800 | 0.940 | 0.860 |
| inactive HOR (hor) | 0.975 | 0.980 | 0.950 | 0.930 | 0.950 | 0.975 |
| active HOR (kinetochore) | 0.950 | 0.930 | 0.925 | 0.930 | 0.865 | 0.900 |
| mixed alpha | 1.000 | 1.000 | 0.380 | 0.375 | 1.000 | 1.000 |
| HSat1A | 0.810 | 0.820 | 0.820 | 0.775 | 0.815 | 0.725 |
| HSat1B | 0.990 | 0.915 | 0.955 | 0.940 | 0.315 | 0.940 |
| HSat2 | 0.475 | 0.650 | 0.515 | 0.615 | 0.690 | 0.750 |
| HSat3 | 0.825 | 0.865 | 0.825 | 0.765 | 0.900 | 0.850 |
| beta satellite (bSat) | 0.870 | 0.875 | 0.855 | 0.810 | 0.845 | 0.895 |
| gamma satellite (gSat) | 0.715 | 0.655 | 0.765 | 0.770 | 0.690 | 0.745 |
| other censat | 0.890 | 0.860 | 0.890 | 0.870 | 0.795 | 0.895 |
| rDNA | 0.255 | 0.210 | 0.415 | 0.540 | 0.500 | 0.075 |
| all | 0.792 | 0.797 | 0.761 | 0.780 | 0.783 | 0.764 |
No setting dominates. Longer k-mers help unique sequence (k = 31: 0.83) and rDNA (0.54) and hurt HSat3 and the "mixed alpha" class, which collapses from 1.00 to 0.38 at k ≥ 25; shorter k-mers help HSat2 (k = 17: 0.65); 5 kb windows help HSat2, HSat3, diverged HORs and monomeric alpha-satellite while breaking HSat1B (0.32) and losing 9 points in active HOR arrays; 20 kb windows help HSat2 and cost 9 points in unique sequence. No single setting is clearly best overall (k = 17 and the default differ by half a point), and the class-specific pattern suggests that a per-class choice of k, or several k, would gain more than any single setting.
4.10 Compute
3,800 reads (76 Mbp) placed on the 6.0 Gbp diploid assembly, 5 threads, no other load.
| method | wall | peak RSS | per read | note |
|---|---|---|---|---|
| sketchy | 69 s | 5.2 GB | 18.1 ms | index prebuilt (see below) |
minimap2 map-ont |
315 s | 30.8 GB | 82.9 ms | builds its reference index in the same run (no .mmi) |
| MashMap3 | 275 s | 46.5 GB | 72.3 ms | likewise |
Winnowmap2 map-ont |
789 s | 13.1 GB | 207.7 ms | likewise; its meryl k-mer list took a further ~10 min |

The aligners' times include building their reference index, which is how they are used on
a new assembly; adding sketchy's index build (142 s, 22.6 GB peak) to its query time (211 s
in total) still leaves it ahead of each of them, and at query time it is the lightest on
memory by a factor of 2.5 to 9. On a prebuilt .mmi, minimap2's mapping alone would be two
to three times faster than shown. Index sizes with every position stored: T2T-CHM13v2.0
3.58 GB, HG002 v1.1 diploid 5.67 GB, one haplotype 3.30 GB.
4.11 Assembly segments on the acrocentric short arms
The short arms of the five acrocentric chromosomes (13, 14, 15, 21 and 22) hold the rDNA arrays, HSat3 and HSat1 blocks, beta satellite and a distal region shared by all five arms (Floutsakou et al. 2013). They were sequenced for the first time in T2T-CHM13 (Nurk et al. 2022), and pangenome analysis has since shown that the arms exchange sequence with one another: over large stretches an arm is more similar to a heterologous acrocentric than to its homologue in another genome (Guarracino et al. 2023). Placing an assembly on the reference in these arms is therefore a different problem from placing reads on the sample's own genome, and the read benchmark above does not answer it.
The test cuts the short arm of each acrocentric chromosome of both HG002 v1.1 haplotypes, telomere to active HOR array, into non-overlapping 100 kb tiles (1,061 tiles; 320 tiles that fall in the assembly's unresolved rDNA gaps are dropped), and places them with every method, using the aligners' assembly presets, in three settings: on the diploid assembly they come from (exact truth; a placement on the other haplotype at the lifted position counts, as in §4.3), on the paternal haplotype alone (maternal tiles), and on CHM13. Across genomes the reference standard is the position lifted through a chromosome-scale alignment, each complete HG002 acrocentric chromosome aligned onto CHM13 with the whole chromosome as context, and a placement is "right" within 10 kb of it. A placement on the right chromosome elsewhere, on the short arm of another acrocentric, or anywhere else, is counted separately.
| method | placed | right chromosome | any acrocentric p-arm | within 10 kb (either haplotype) | own haplotype | confident and wrong |
|---|---|---|---|---|---|---|
| minimap2 | 1.000 | 0.987 | 1.000 | 0.978 (0.97-0.99; n=1061) | 0.978 | 0.000 (n=721) |
| winnowmap2 | 1.000 | 0.983 | 1.000 | 0.975 (0.96-0.98; n=1061) | 0.975 | 0.000 (n=997) |
| mashmap3 | 1.000 | 0.881 | 1.000 | 0.733 (0.71-0.76; n=1061) | 0.666 | 0.006 (n=165) |
| sketchy | 1.000 | 0.961 | 1.000 | 0.936 (0.92-0.95; n=1061) | 0.926 | 0.019 (n=645) |
| method | placed | homologous chromosome | right array | any acrocentric short arm | same chromosome as the chromosome-scale alignment | within 10 kb of it | confident and heterologous |
|---|---|---|---|---|---|---|---|
| minimap2 | 1.000 | 0.577 | 0.538 | 0.983 | 0.702 (n=476) | 0.149 (0.12-0.18) | 0.358 (n=279) |
| winnowmap2 | 1.000 | 0.596 | 0.555 | 0.987 | 0.721 (n=476) | 0.139 (0.11-0.17) | 0.383 (n=449) |
| mashmap3 | 1.000 | 0.555 | 0.508 | 0.993 | 0.676 (n=476) | 0.120 (0.09-0.15) | 0.347 (n=167) |
| sketchy | 1.000 | 0.549 | 0.516 | 0.994 | 0.674 (n=476) | 0.149 (0.12-0.18) | 0.333 (n=159) |

| method | placed | homologous chromosome | right array | any acrocentric short arm | same chromosome as the chromosome-scale alignment | within 10 kb of it | confident and heterologous |
|---|---|---|---|---|---|---|---|
| minimap2 | 1.000 | 0.556 | 0.525 | 0.989 | 0.783 (n=990) | 0.255 (0.23-0.28) | 0.402 (n=712) |
| winnowmap2 | 1.000 | 0.563 | 0.533 | 0.988 | 0.773 (n=990) | 0.253 (0.23-0.28) | 0.412 (n=988) |
| mashmap3 | 1.000 | 0.549 | 0.504 | 0.992 | 0.742 (n=990) | 0.237 (0.21-0.26) | 0.313 (n=469) |
| sketchy | 1.000 | 0.547 | 0.515 | 0.978 | 0.738 (n=990) | 0.233 (0.21-0.26) | 0.353 (n=425) |
| class | n | minimap2: homologous chr / right array / chain chr / chain position | winnowmap2: homologous chr / right array / chain chr / chain position | mashmap3: homologous chr / right array / chain chr / chain position | sketchy: homologous chr / right array / chain chr / chain position |
|---|---|---|---|---|---|
| ct | 364 | 0.538 / 0.497 / 0.680 / 0.227 (362) | 0.538 / 0.497 / 0.663 / 0.221 (362) | 0.519 / 0.475 / 0.613 / 0.221 (362) | 0.533 / 0.492 / 0.644 / 0.213 (362) |
| hsat3 | 260 | 0.738 / 0.715 / 0.909 / 0.311 (241) | 0.742 / 0.723 / 0.896 / 0.320 (241) | 0.735 / 0.704 / 0.880 / 0.307 (241) | 0.746 / 0.723 / 0.900 / 0.303 (241) |
| hsat1A | 133 | 0.286 / 0.286 / 0.912 / 0.275 (91) | 0.293 / 0.293 / 0.846 / 0.275 (91) | 0.301 / 0.271 / 0.912 / 0.253 (91) | 0.211 / 0.211 / 0.747 / 0.220 (91) |
| mon | 90 | 0.800 / 0.678 / 0.787 / 0.225 (89) | 0.856 / 0.733 / 0.831 / 0.236 (89) | 0.789 / 0.644 / 0.809 / 0.258 (89) | 0.767 / 0.633 / 0.764 / 0.202 (89) |
| bsat | 88 | 0.216 / 0.216 / 0.852 / 0.346 (81) | 0.205 / 0.205 / 0.827 / 0.321 (81) | 0.216 / 0.216 / 0.728 / 0.284 (81) | 0.239 / 0.239 / 0.765 / 0.321 (81) |
| censat | 41 | 0.293 / 0.293 / 0.488 / 0.171 (41) | 0.293 / 0.293 / 0.463 / 0.049 (41) | 0.220 / 0.220 / 0.512 / 0.073 (41) | 0.293 / 0.293 / 0.415 / 0.098 (41) |
| hor | 39 | 0.897 / 0.897 / 0.974 / 0.231 (39) | 0.897 / 0.897 / 0.923 / 0.179 (39) | 0.872 / 0.821 / 0.897 / 0.128 (39) | 0.897 / 0.897 / 0.974 / 0.154 (39) |
| hsat1B | 25 | 0.960 / 0.960 / 1.000 / 0.080 (25) | 0.960 / 0.960 / 1.000 / 0.200 (25) | 0.960 / 0.840 / 0.960 / 0.080 (25) | 0.960 / 0.960 / 1.000 / 0.160 (25) |
| rDNA | 16 | 0.062 / 0.062 / 0.250 / 0.188 (16) | 0.125 / 0.125 / 0.375 / 0.188 (16) | 0.250 / 0.250 / 0.375 / 0.062 (16) | 0.125 / 0.125 / 0.125 / 0.125 (16) |
| unique | 5 | 0.200 / 0.000 / 0.200 / 0.200 (5) | 0.200 / 0.000 / 1.000 / 0.800 (5) | 0.400 / 0.000 / 0.200 / 0.200 (5) | 0.200 / 0.000 / 0.200 / 0.200 (5) |
| ALL | 1061 | 0.556 / 0.525 / 0.783 / 0.255 (990) | 0.563 / 0.533 / 0.773 / 0.253 (990) | 0.549 / 0.504 / 0.742 / 0.237 (990) | 0.547 / 0.515 / 0.738 / 0.233 (990) |

| class | n | best CHM13 arm is the homolog | margin < 0.5 pp | margin < 2 pp | minimap2: homologous chr when margin < 0.5 / >= 0.5 | winnowmap2: homologous chr when margin < 0.5 / >= 0.5 | mashmap3: homologous chr when margin < 0.5 / >= 0.5 | sketchy: homologous chr when margin < 0.5 / >= 0.5 |
|---|---|---|---|---|---|---|---|---|
| ct | 364 | 0.541 | 0.453 | 0.615 | 0.497 / 0.573 | 0.473 / 0.593 | 0.406 / 0.613 | 0.461 / 0.593 |
| hsat3 | 260 | 0.738 | 0.065 | 0.115 | 0.294 / 0.770 | 0.353 / 0.770 | 0.235 / 0.770 | 0.412 / 0.770 |
| hsat1A | 133 | 0.308 | 0.030 | 0.090 | 0.250 / 0.287 | 0.000 / 0.302 | 0.000 / 0.310 | 0.250 / 0.209 |
| mon | 90 | 0.767 | 0.300 | 0.433 | 0.519 / 0.921 | 0.593 / 0.968 | 0.593 / 0.873 | 0.481 / 0.889 |
| bsat | 88 | 0.239 | 0.148 | 0.261 | 0.000 / 0.253 | 0.000 / 0.240 | 0.000 / 0.253 | 0.077 / 0.267 |
| censat | 41 | 0.268 | 0.268 | 0.439 | 0.091 / 0.367 | 0.091 / 0.367 | 0.091 / 0.267 | 0.091 / 0.367 |
| hor | 39 | 0.872 | 0.051 | 0.077 | 1.000 / 0.892 | 1.000 / 0.892 | 0.500 / 0.892 | 1.000 / 0.892 |
| hsat1B | 25 | 0.960 | 0.000 | 0.000 | -- / 0.960 | -- / 0.960 | -- / 0.960 | -- / 0.960 |
| rDNA | 16 | 0.188 | 0.438 | 0.938 | 0.143 / 0.000 | 0.143 / 0.111 | 0.429 / 0.111 | 0.143 / 0.111 |
| unique | 5 | 0.400 | 0.200 | 0.600 | 0.000 / 0.250 | 1.000 / 0.000 | 0.000 / 0.500 | 0.000 / 0.250 |
| ALL | 1061 | 0.560 | 0.233 | 0.346 | 0.429 / 0.595 | 0.425 / 0.604 | 0.372 / 0.603 | 0.413 / 0.587 |
| class | n | nearest copy on the other haplotype is the homologue | is a heterologous arm | no copy reported | median matched fraction, homologue | median matched fraction, nearest heterologous arm |
|---|---|---|---|---|---|---|
| ct | 364 | 0.560 | 0.440 | 0.000 | 0.994 | 0.991 |
| hsat3 | 260 | 0.712 | 0.285 | 0.004 | 0.961 | 0.000 |
| hsat1A | 133 | 0.391 | 0.609 | 0.000 | 0.261 | 0.356 |
| mon | 90 | 0.711 | 0.289 | 0.000 | 0.997 | 0.949 |
| bsat | 88 | 0.420 | 0.580 | 0.000 | 0.622 | 0.836 |
| censat | 41 | 0.122 | 0.878 | 0.000 | 0.000 | 0.984 |
| hor | 39 | 0.897 | 0.103 | 0.000 | 0.991 | 0.000 |
| hsat1B | 25 | 0.880 | 0.120 | 0.000 | 0.983 | 0.000 |
| rDNA | 16 | 0.188 | 0.812 | 0.000 | 0.973 | 0.985 |
| unique | 5 | 0.200 | 0.800 | 0.000 | 0.810 | 0.957 |
| ALL | 1061 | 0.573 | 0.426 | 0.001 | 0.974 | 0.876 |
On the assembly the tiles come from, with exact sequence, minimap2 and Winnowmap2 return 98% of the short-arm tiles to within 10 kb of their origin on either haplotype, composition 94% and MashMap3 73% (88% on the right chromosome; its coordinates are approximate at the scale of its 5 kb segments). Every method keeps every tile on an acrocentric short arm. Within one assembly, then, the short arms are resolvable at the 100 kb scale, and the few tiles each method sends to another arm are those whose closest copy in HG002 lies on another arm.
On the paternal haplotype the four methods still agree with one another (minimap2 and sketchy name the same chromosome for 81% of the maternal tiles), but only 55 to 60% of the tiles land on the paternal homologue, although 98 to 99% stay on an acrocentric short arm. This is not an error of tile-scale mapping. Aligning each maternal chromosome in full onto the paternal haplotype sends 34% of the liftable tiles (160 of 476) to a heterologous arm as well, and the tile placements agree with that chromosome-scale standard on the chromosome for 67 to 72% of tiles. Inside satellite arrays a tile rarely reproduces the chromosome-scale position (12 to 15% within 10 kb), as with reads in §4.5: the arrays differ between haplotypes in copy number and order, not only in sequence.
On CHM13 the picture repeats with a second genome: 55 to 56% of tiles on the homologous chromosome for every method, 98 to 99% on an acrocentric short arm, 74 to 78% on the chromosome the chromosome-scale alignment chooses and 23 to 26% at its position. The exchanges are chromosome-specific (table below): HG002 chr21 tiles go to CHM13 chr13 more often than to chr21 (80 against 49 for minimap2), chr13 tiles go to chr22 and chr21, chr14 tiles to chr22 and chr21, while chr15 and chr22 tiles mostly stay (77% and 70%). By class (minimap2; the other methods are within a few points), HSat1A (29%), beta satellite (22%), other censat (29%) and rDNA (6%) are rarely placed on the homologue; HSat3 (74%), monomeric alpha-satellite (80%), inactive HOR (90%) and HSat1B (96%) mostly are; the centromere-transition class, a third of all tiles, splits evenly (54%).
| source | n | method | chr13 | chr14 | chr15 | chr21 | chr22 | other | unplaced |
|---|---|---|---|---|---|---|---|---|---|
| chr13 | 216 | minimap2 | 85 | 18 | 25 | 35 | 48 | 5 | 0 |
| chr13 | 216 | winnowmap2 | 92 | 16 | 22 | 30 | 54 | 2 | 0 |
| chr13 | 216 | mashmap3 | 90 | 10 | 21 | 28 | 64 | 3 | 0 |
| chr13 | 216 | sketchy | 79 | 18 | 20 | 32 | 50 | 17 | 0 |
| chr14 | 240 | minimap2 | 17 | 127 | 5 | 33 | 57 | 1 | 0 |
| chr14 | 240 | winnowmap2 | 19 | 134 | 4 | 28 | 53 | 2 | 0 |
| chr14 | 240 | mashmap3 | 26 | 124 | 11 | 29 | 50 | 0 | 0 |
| chr14 | 240 | sketchy | 22 | 126 | 7 | 34 | 50 | 1 | 0 |
| chr15 | 251 | minimap2 | 7 | 11 | 192 | 10 | 25 | 6 | 0 |
| chr15 | 251 | winnowmap2 | 7 | 13 | 188 | 12 | 25 | 6 | 0 |
| chr15 | 251 | mashmap3 | 5 | 18 | 186 | 11 | 26 | 5 | 0 |
| chr15 | 251 | sketchy | 4 | 15 | 192 | 12 | 26 | 2 | 0 |
| chr21 | 158 | minimap2 | 80 | 7 | 10 | 49 | 12 | 0 | 0 |
| chr21 | 158 | winnowmap2 | 77 | 11 | 14 | 48 | 7 | 1 | 0 |
| chr21 | 158 | mashmap3 | 79 | 13 | 7 | 48 | 11 | 0 | 0 |
| chr21 | 158 | sketchy | 80 | 12 | 9 | 47 | 9 | 1 | 0 |
| chr22 | 196 | minimap2 | 5 | 16 | 36 | 2 | 137 | 0 | 0 |
| chr22 | 196 | winnowmap2 | 7 | 15 | 35 | 2 | 135 | 2 | 0 |
| chr22 | 196 | mashmap3 | 6 | 19 | 36 | 0 | 135 | 0 | 0 |
| chr22 | 196 | sketchy | 7 | 16 | 30 | 5 | 136 | 2 | 0 |
The identity margins explain these numbers. The best-matching CHM13 arm is the homologue for 56% of tiles, the fraction every method places there, and for 23% of tiles the best and the second-best arm are within 0.5 percentage points of matched fraction (35% within 2 points). Where the margin is clear the methods place 59 to 60% of tiles on the homologue, where it is not, 37 to 43%. The methods find the closest sequence, and the closest sequence is on another chromosome for two tiles in five. Mapping quality does not flag this: 40% of minimap2's placements at mapq >= 30, 41% of Winnowmap2's and 35% of sketchy's at the equivalent posterior are on a heterologous arm, because mapping quality measures how far the best hit beats the runner-up, not which chromosome it is on.
The same structure exists inside HG002 itself. Aligning every tile against the other haplotype with all hits kept, the nearest copy is the homologue for 57% of tiles and a heterologous arm for 43% (table above; a copy that is not reported scores below half of the best one). The classes divide as they do on CHM13: HSat3, monomeric alpha-satellite, inactive HOR and HSat1B have their nearest copy on the homologue (71 to 90%), while HSat1A, beta satellite, other censat and rDNA have it on a heterologous arm (58 to 88%), and the centromere-transition class splits (56% homologue) with median matched fractions of 0.994 and 0.991 to the two. The tiles that a method sends to another arm even on the tile's own assembly are mostly those whose nearest other-haplotype copy is heterologous (9 of minimap2's 14, 30 of sketchy's 41).
For an assembly, then, the chromosome identity of a short-arm segment cannot be read from
the segment. By similarity, any method assigns 45% of 100 kb segments to a chromosome other
than their own, and even the chromosome-scale standard used here, which carries the
centromere and the long arm as context, assigns 35% of them to a heterologous arm. Composition has no advantage in
this regime: it follows the same similarity to the same answers, at a fraction of the cost
(41 s for the 1,061 tiles on CHM13 against 103 s for MashMap3, 168 s for minimap2 and 874 s
for Winnowmap2, 8 threads). The per-tile results of all three settings are kept in
docs/v2/acro/ as a resolvability track of the HG002 v1.1 short arms: which segments every
method places consistently, which are ambiguous by identity, and where the chromosome-scale
alignment disagrees with the tile.
5. Discussion
The results separate three questions that v0.1 conflated. Placement of reads on the sample's own complete assembly is solved by alignment in every satellite class at current error rates; the difficulty attributed to satellites belongs to incomplete references and to cross-haplotype placement. Across haplotypes and individuals, array-level placement is bounded by the biology and is the same for composition and alignment. Within-array order across haplotypes, the one measurement without a truth set, favours composition on one contig pair and is a tie on another.
Composition's practical value is therefore not accuracy. It is a placement at about 80%
of an aligner's accuracy at one fifth of its time and one sixth of its memory, with a posterior that is
calibrated on average, and with an explicit credible set (cred_span, cred_loci) that
states which array a read belongs to when its position within it cannot be resolved. Those
properties suit triage, read recruitment to arrays, and screening before alignment rather
than replacement of it. The benchmark is the more general contribution.
The acrocentric test (§4.11) adds a limit that no placement method can move. On the
sample's own assembly, 100 kb segments of the short arms are resolved to 98% by alignment
and 94% by composition; on another genome, every method puts 55% of them on their own
chromosome, because for 44% of the segments the closest arm in the reference is a
heterologous one. The chromosome-scale alignment, which carries the centromere and long arm
as context, still assigns a third of them to a heterologous arm. Chromosome assignment of short-arm
sequence is a property of whole chromosomes, and a mapper's confidence, which measures how
far the best hit beats the runner-up, says nothing about it. For an assembly the practical
consequences are to place short-arm contigs with their flanking centromere and long arm
attached, to read a heterologous placement as a statement about similarity rather than
identity, and to treat the resolvability track in docs/v2/acro/ as a map of where the
reference can and cannot decide.
6. Limitations
- The error model is simple and does not reproduce nanopore's context dependence; the real-read test is agreement, not truth. Measured from minimap2's CIGARs, real R10.4.1 reads have a lower indel rate than the simulator (0.4% against 1.0% per base) but an alignment diagonal that wanders 5-13 times further than a random walk of independent indels predicts (90th-percentile excursion 433 bp per 77 kb read, against 36 bp per simulated 20 kb read), because their indels are clustered. The fine stage's smoothing width, which the simulations alone would set three times narrower, was therefore set against real reads (agreement with minimap2 78.4% against 75.4%), at a cost of 0.6 points on the simulated benchmark.
- Within-array resolution in HSat2 and rDNA remains poor (48% and 26%); alignment resolves both, so the information is present in the reads.
- The posterior is calibrated on average but does not isolate a near-perfect subset; confident errors are the wrong copy of a near-identical repeat.
- Haplotype phasing in unique sequence is limited by sketch density; in an earlier configuration a budget of 512 raised overall accuracy by about 5 points at a 34% larger index (7.6 GB), and the repeat-aware pass recovers most of that at 5.7 GB.
- All exact-truth results are from one genome (HG002) and one machine; the walks are two contig pairs.
- The acrocentric test is one individual (HG002) against one reference (CHM13), at one tile length. HG002 v1.1 leaves most of its rDNA arrays as gaps (320 of the 1,381 tiles were dropped for that reason), so rDNA is represented by array flanks only, and CHM13's rDNA arrays are model sequence. The chromosome-scale alignment used as the reference standard across genomes is itself a mapping, chosen for the context it carries rather than for any truth it has.
7. Reproducibility
cargo build --release && cargo test --release # 30 tests, incl. end-to-end placement on a synthetic genome
scripts/fetch_data.sh --assemblies # CHM13 + annotation, HPRC assemblies (~4 GB)
curl -fL -o data/hg002v1.1.fasta.gz https://s3-us-west-2.amazonaws.com/human-pangenomics/T2T/HG002/assemblies/hg002v1.1.fasta.gz
curl -fL -o data/hg002v1.1.cenSatv2.0.bed https://s3-us-west-2.amazonaws.com/human-pangenomics/T2T/HG002/assemblies/annotation/centromere/hg002v1.1_v2.0/hg002v1.1.cenSatv2.0.bed
# the exact-truth benchmark, end to end (any mapper can be added as a PAF in <outdir>/bench/)
bench/cenbench.sh data/hg002v1.1.fasta.gz data/hg002v1.1.cenSatv2.0.bed out/cenbench 6 ont-r10 20000 200 1000
# the tables in this file
.venv/bin/python bench/fill_tables.py
The exact command lines behind every table are in docs/METHODS.md §9;
bench/README.md describes each script. Winnowmap2 is built from source on Apple silicon
as described in v0.1's README (git history).
Output columns
| column | meaning |
|---|---|
chrom start end strand |
the call; start is the leftmost reference coordinate |
mapq |
-10 log10(1 - post), capped at 60 |
post |
posterior of the call: the chosen region's posterior times the mass within 2 kb of the fine peak |
coarse_start coarse_post |
the composition-only call and its mass within one window |
region_post |
posterior of the chosen region after the fine update |
llr gap |
coarse evidence at the peak, and its gap to the best start outside the guard region (nats) |
cred_span cred_loci cred_frac |
the 90% credible set: bp span, separate runs, fraction of the genome |
p_hat seen_frac |
estimated k-mer survival; fraction of the query's k-mers the index has seen |
fine_ok fine_post fine_votes |
whether the fine stage anchored, its within-region mass, anchors under the peak |
candidates |
credible runs with their masses |
Repository layout
src/ seq.rs (BGZF reader) · kmer.rs · sketch.rs · index.rs · locate.rs · sim.rs · main.rs
bench/ cenbench.sh, run_methods.sh, score_v2.py, summarize_sim.py, score_xhap.py, score_agreement.py,
paired_test.py, liftover.py, fill_tables.py, fig_v2.py, sweep.py + make_walk.py (walks),
acro_tiles.py + score_acro.py + fig_acro.py (acrocentric short arms, §4.11), v1/ (v0.1 scripts)
docs/ METHODS.md, REVIEW.md, v2/ figures, v2/acro/ per-tile short-arm results, index.html (built by bench/build_site_v2.py)
tests/ end-to-end tests on a synthetic genome
References
- Altemose N, Logsdon GA, Bzikadze AV, et al. Complete genomic and epigenetic maps of human centromeres. Science 2022.
- Bzikadze AV, Pevzner PA. centroFlye. Nat Biotechnol 2020.
- Floutsakou I, Agrawal S, Nguyen TT, et al. The shared genomic architecture of human nucleolar organizer regions. Genome Res 2013.
- Guarracino A, Buonaiuto S, de Lima LG, et al. Recombination between heterologous human acrocentric chromosomes. Nature 2023.
- Jain C, Koren S, Dilthey A, Phillippy AM, Aluru S. MashMap. Bioinformatics 2018.
- Jain C, Rhie A, Hansen NF, Koren S, Phillippy AM. Winnowmap2. Nat Methods 2022.
- Kille B, et al. MashMap3. Bioinformatics 2023.
- Li H. minimap2. Bioinformatics 2018.
- Lipman DJ, Pearson WR. FASTA. Science 1985.
- Logsdon GA, et al. The variation and evolution of complete human centromeres. Nature 2024.
- Mikheenko A, Bzikadze AV, et al. TandemTools / VerityMap. Bioinformatics 2020; Genome Res 2022.
- Nurk S, Koren S, Rhie A, et al. The complete sequence of a human genome. Science 2022.
- Telomere-to-Telomere Consortium. HG002 v1.1, a complete diploid human genome assembly. github.com/marbl/HG002; human-pangenomics S3, 2024.
- Shiraishi Y, et al. ascairn. 2025.
- Suzuki H, et al. mm2-ivh: interval hashing for alpha-satellite HORs. bioRxiv 2025.
- A global view of human centromere variation and evolution. Nature 2026.
Authorship
v0.2 was written by Claude Fable 5.1 (Anthropic) in autonomous sessions on 2026-09-01 and 2026-09-02 for @jlanej, who set the problem, supplied the data and the machine, and directed the follow-up questions (the scientific-register pass, the mechanism tests, the acrocentric short-arm test in §4.11 and the cleanup). It builds on v0.1 by Claude Opus 5 and @jlanej. Claude designed the method, the benchmark, the scoring and the figures, ran every experiment, and wrote this document, docs/METHODS.md and docs/REVIEW.md; @jlanej reviewed the results. MIT licensed.
Review of sketchy v0.1, and what v0.2 changes
This is the review that motivated the rewrite. It was written after reading every source file, every benchmark script and every number in the v0.1 README, and after re-running the v0.1 tool and its scoring scripts to confirm that the published numbers reproduce (they do: 94.1% of HG002 fragments within 100 kb, 60.8% for alpha-satellite HOR, 42.1% for HSat2).
In summary, the empirical work in v0.1 was careful and largely correct about its own limits; the method it evaluated was poorly matched to the problem, and the benchmark needed to decide the question it posed was absent. Version 0.2 addresses both.
1. What v0.1 got right
- The density-equalizing sketch. Keeping a window's whole vocabulary when it is small (satellite arrays: ~2,300 distinct 21-mers per 10 kb) and a fixed budget when it is large (unique sequence: ~9,950) is the right allocation for localization, and v0.1 measured it fairly against bottom-k and FracMinHash at matched index bytes. This idea survives unchanged into v0.2.
- Transparent reporting. Four headline claims were retracted in the git history after
adversarial review; confidence columns were audited and one (
p_value) was found to be a constant and removed; the fragment benchmark's cluster structure (design effect 5.9) was measured and reported. Version 0.2 retains these practices. - The truth-free centromere walk (cut a contig into consecutive pieces, place each independently, score the order) is a genuinely useful design for comparing localizers where no truth set exists, and its metric self-test is sound. It is kept as-is.
- The measurement that locus-private k-mers transfer between haplotypes only 2-8% of the time in satellite arrays (probes 8 and 9) is the most important scientific fact in the repository. It says that any method anchoring on rare k-mers -- VerityMap, TandemMapper, centroFlye, and v0.1 itself -- is bounded a priori when the query comes from a different haplotype than the reference. v0.2 is designed around that fact rather than against it.
2. What was wrong with the method
2.1 The score was a heuristic with no probabilistic reading
v0.1 scored a tile against a window by a tf-idf cosine with the rarity weight raised to
the power 2.5 in the numerator but power 1 in the reference norm. The exponent was tuned
on the benchmark, and the norm asymmetry was chosen because the symmetric form scored
worse. The resulting score was, as the README stated, "not bounded by 1, not comparable
between queries". Every confidence statistic (snr, margin, tile_support,
support_span, rival_frac) was then a post-hoc function of that score, and the audit
found that support_span covered the truth only 26-65% of the time inside satellites.
A cosine is a similarity, not a likelihood. Nothing in it says how probable it is that
the query came from this window rather than that one, so nothing downstream could be
calibrated except by fitting a separate model on top (the split-conformal interval in
stat_audit.py, which was never wired into the tool).
Version 0.2 replaces the score with identity evidence that has a direct probabilistic
reading: each non-overlapping segment of shared k-mers contributes ln(N/df) for its rarest
word, the log-odds of observing that word in the true window rather than in one drawn at
random. (A term penalising unexplained words, the textbook likelihood, was implemented and
rejected; docs/METHODS.md §3 gives the measurement.) The profile over windows becomes a
posterior through a softmax whose temperature is fitted on reads whose origin is known
exactly. The posterior mass of the call is the confidence and its Phred form is mapq;
the README's Section 4.4 reports its calibration on held-out error models and read lengths.
2.2 The query was sketched, and that created a problem that then needed three fixes
The reference must be sketched (that is what makes the index 2 GB rather than 30 GB). The
query does not need to be: it is one sequence, its k-mers cost nothing to enumerate, and
looking up ten thousand hashes takes a millisecond. v0.1 nevertheless sketched the query
by a rule of its own -- first the "ramp", then a per-query blend between ramp and step,
chosen from seen_frac -- and then discovered that sketching the query more densely than
the reference biases the cosine (the norms are over different hash spaces), tried a
per-window norm correction, found pruning worked better, and shipped the blend. Roughly a
third of locate.rs and of the README's method section dealt with this.
Version 0.2 does not sketch the query. Every query k-mer is looked up; a window can share
only the words whose hash lies at or below its own threshold, a prefix of the hash space
that is exactly what the reference stored, so no correction is required. The ramp, the
step, the blend, --query-blend, --per-window-norm and seen_frac-driven pruning are
removed.
2.3 The link channel did not earn its half of the index
The order-aware "link" channel (hashed pairs of consecutive minimizers) was the only positional information in v0.1. The matched ablation put its contribution at +0.009 with p = 0.29, and it accounted for 94 M of the 222 M postings. It was an indirect way to recover order from a bag-of-words model.
Version 0.2 stores every position of every retained k-mer (u32 with a strand bit) and
runs a positional stage inside the candidate regions of the coarse posterior: every query
k-mer votes for the diagonal ref - query of every stored occurrence, weighted by
ln(N/df). This is the FASTA diagonal method (1985) with rarity weights, which v0.1's
prior-art section itself named, and it gives base-level resolution, the strand (including
for single-window queries, which v0.1 could not orient) and a second calibrated posterior.
2.4 Resolution was bounded by the window
v0.1's median error in unique sequence was 2.2-2.5 kb, a consequence of snapping to a 10 kb grid and interpolating rather than a property of the sequence. The fine stage removes that floor; on clean sequence v0.2 places to the base.
3. What was wrong with the evaluation
3.1 The benchmark could not answer the question posed
Every real-data number in v0.1 scores HG002 fragments against CHM13, with truth from minimap2. Inside a satellite array that conflates three quantities: the method's error, the aligner's error (78% of alpha-satellite truth calls carried mapq ≤ 5), and the genuine difference between two haplotypes' arrays, which Logsdon et al. (2024) quantify as 45.8% of centromeric sequence being unalignable between haplotypes. v0.1 acknowledged this, reported satellite accuracy as a range rather than a number, and ran one small within-sample test (59 satellite reads on the HG002 HPRC draft assembly, whose centromeres are incomplete).
Version 0.2 uses the T2T HG002 v1.1 diploid assembly, in which both haplotypes are complete through every centromere and which carries its own cenSat annotation, and simulates reads from it with stated error models. The origin of every base is then known exactly, inside the centromere and out, for both haplotypes. This benchmark can determine whether composition places a read within its array, at what resolution, and whether an aligner does. The cross-haplotype benchmarks (HG002 and HG02723 fragments against CHM13) are retained as the second question: what remains recoverable when the reference is not the sample's haplotype.
3.2 The failure of alignment was inferred from one contig walk
The claim that alignment fails in satellites rested in v0.1 on the walk, where sketchy's order statistic exceeded every aligner's inside the HOR array, and on the mapq of the truth calls. Neither measures an aligner's accuracy in a satellite, because neither had a truth. With exact-truth simulated reads, v0.2 measures minimap2, Winnowmap2 and MashMap3 on the same reads, by region class, with wall time and peak memory recorded, and reports the aligners' advantage where it exists.
3.3 Simulation was dismissed too early
v0.1 concluded that simulated divergence is a structurally different and easier problem after finding that tiling helped on simulated data and not on real cross-haplotype data. That holds for cross-haplotype placement, where the difference is the haplotype rather than the error model. It is not a reason to avoid simulation for the same-haplotype question, where simulation is the only route to exact truth inside a satellite. v0.2 uses both and states which question each answers.
4. Compute
| v0.1 | v0.2 (final: every position stored, repeat-aware second pass) | |
|---|---|---|
| index, CHM13v2.0 | 2.87 GB, 65 s, 12 GB peak | 3.58 GB, 95 s, 10.9 GB peak |
| index, HG002 v1.1 (6.0 Gbp diploid) | -- | 5.67 GB, 142 s, 22.6 GB peak |
| feature channels | 2 (k-mer + minimizer pairs) | 1, with every position |
| query sketching | ramp/step/blend, per query | none |
| per-query cost | tiles × postings, plus a genome-wide dense pass per tile | tiles × postings (sparse), one lookup per query k-mer, and a region-restricted diagonal vote |
An intermediate v0.2 index with one position per word was 2.05 GB for CHM13 and built in 34 s; storing every position (+50%) is what bought within-array resolution, and the repeat-aware second pass adds another 17% for a five-point gain in accuracy. Query cost is measured in the write-up alongside the aligners on identical inputs.
5. What v0.2 does not change
The density-equalizing keep rule; the 10 kb window; the inverted index with a 22-bit bucket table over sorted keys; the block-parallel BGZF reader; the benchmark harness, samplers and the truth-free sweep. The README's prior-art section was accurate and is carried forward.
6. Claims from v0.1 that v0.2 retires
- "
margin > 3is the column to filter on" -- replaced by a calibrated posterior; filter onmapqorpostlike any other mapper. - "
snr,tile_support,support_span,rival_frac" -- gone, along with the audit that showed which of them meant what.cred_span/cred_loci(the 90% credible set) carry the shape information with a coverage guarantee the old columns lacked. - "the query keep rule is chosen per query" -- there is no query keep rule.
- "orientation needs more than one tile" -- the fine stage orients single-tile queries.
Review and rewrite by Claude Fable 5.1 (Anthropic) for @jlanej, September 2026.
Methods (v0.2)
sketchy places a query sequence on a reference genome using its k-mer composition, and
reports a posterior probability for the call. Alignment appears in this repository only
offline, to establish ground truth for the cross-haplotype benchmarks, and as a competitor
in the benchmarks themselves. It is never available to the method.
The version-2 method has four parts: a density-equalizing reference sketch (unchanged from v0.1), a coarse stage that scores windows by composition, a fine stage that resolves position by rare-word diagonals inside candidate regions, and a calibrated posterior that combines the two. Each is stated here in full.
1. Features
Every canonical k-mer (default k = 21) is hashed with a splitmix64 finalizer. Canonical
means the lexicographically smaller of the k-mer and its reverse complement, so a sequence
and its reverse complement produce identical hash multisets. Alongside each hash the index
keeps whether the forward k-mer was the canonical one (one bit), which is what lets the
fine stage recover strand by comparing that bit between query and reference. A non-ACGT base
resets the rolling window.
2. The reference index: a density-equalizing sketch, with positions
The reference is cut into windows of W bp (default 10 kb). A window's distinct k-mers are
ranked by hash and the smallest are kept -- a bottom-k sketch -- with the keep count set by
the step rule:
keep(n) = n if n <= V^2 / (2V - budget) (the crossover)
= budget otherwise
V is the vocabulary of a fully unique window of this size, estimated from the reference
at build time (98th percentile of distinct counts over 4,096 sampled windows; 9,980 for
CHM13 at k = 21, W = 10 kb) and budget is 256. A 10 kb alpha-satellite HOR window holds
about 2,300 distinct 21-mers, an HSat3 window about 4,000, a unique window about 9,950: the
windows that need every word they have are exactly those with few enough words to afford
it. Windows with less than 50% A/C/G/T are not indexed.
Because the kept set is a hash prefix (every hash at or below the window's threshold), two windows sketched at different densities are comparable on the prefix they share, and an unsketched query is comparable to every window on that window's own prefix. That property is what removes any need to sketch the query.
For every kept k-mer the index stores every position at which it occurs in the window (chromosome-local, with the strand bit). v0.1 stored one position per word per window; inside a tandem repeat almost every word occurs several times per window, and a word with one stored position can vote for only one of its copies. The fine stage's within-array resolution depends on this.
On disk the index is one file of little-endian arrays, 64-byte aligned, opened by mmap:
a bucket table over the top 22 bits of each hash, the sorted distinct keys, a CSR offset
array, and per posting the window id (u32), multiplicity (u16) and an offset into the
position array. A key lookup costs about two cache misses.
3. Coarse stage: composition over windows
The query is cut into tiles of W bp (non-overlapping by default). Every k-mer of a tile
is looked up; the query is never sketched.
Evidence. For a tile and a reference window, the identity evidence is
E(w) = sum over non-overlapping shared segments of ln( N / df(h*) )
where df(h) is the number of windows carrying h, N the number of windows, and h* the
rarest word of the segment. A segment is opened by the first shared k-mer at a query
position and absorbs every shared k-mer that starts within k bases of it; the next
segment opens at the next shared k-mer beyond. ln(N/df) is the log-odds that a word would
be seen in a window that carries it against a window drawn at random.
Two choices here are the result of measurements, not taste:
- Segments, not k-mers. Overlapping k-mers are not independent observations. One Alu in a query shares ~300 consecutive k-mers with every window that carries an Alu, and counting each of them let a repeat-dense window that stores its whole vocabulary outscore the true unique window, whose sketch can only ever share a hash-subsample (unique-sequence accuracy 92% against 99% once segments were used). Crediting each segment with its rarest word rather than its first is what lets a private variant k-mer separate two windows of one array that share every common word.
- No penalty for unexplained words. A likelihood model that charges a window
ln(1 - p)for every comparable word it fails to share is the textbook form, and it was tried. It fails on exactly the queries this tool exists for: a read from another haplotype of a satellite array carries many words that exist elsewhere in the array, and charging its true window for them let windows that share nothing win (alpha-satellite HOR right chromosome 73% against 97% without the term). Words the window does not share are therefore uninformative, and a window that shares nothing is excluded from the posterior rather than merely penalised.
Words carried by more than max_df_frac of windows (default 1%) are skipped; they carry
less than 4.6 nats of information each and dominate running time.
Shift-and-sum. A tile at query offset a matching window j is evidence that the query
starts at window j - round(a/W); reverse-complemented, at j - round((qlen - b)/W).
Each tile's evidence is added into forward and reverse profiles over all windows. The
profiles are sparse: only windows that share a segment with some tile are non-zero.
Phase correction. A query whose tiles are out of phase with the reference windows
splits each tile's evidence between two consecutive starts, so its peak is about half of
an in-phase query's. Two haplotypes of one genome are never in phase with each other, and
on the raw profile the choice between them was decided by phase rather than evidence
(haplotype accuracy 53% in unique sequence). The decision profile is therefore
L(s) + L(s+1) on each strand.
Posterior. With temperature T,
P(s) = exp(L(s)/T) / sum over starts and strands with L > 0 of exp(L/T)
The MAP start, its posterior mass within one window (coarse_post), the cred-level
credible set (its bp span and number of separate runs: cred_span, cred_loci), and the
evidence gap to the best start outside the guard region around the MAP (gap, nats) are
reported. T is calibrated (section 6); it is not 1, because segments are still far from
independent.
Candidate regions. Every maximal run of starts whose decision-profile value is at least
40% of the peak, on either strand, is a candidate region, padded by the query's length in
windows plus two so the query can sit anywhere inside. The MAP's region comes first; at most
six are kept. The rule is independent of T on purpose.
3a. k-mer survival
For up to eight tiles spaced along the query, the window with the most evidence is found
and the fraction of the tile's k-mer occurrences it explains, divided by the fraction of
the hash space that window retained, is recorded; p_hat is the median over tiles. For a
read from its own reference this is (1 - e)^k: 0.69 on the 1.5%-error set (expected 0.73),
0.21 on the 7% set (expected 0.22). It sets the fine stage's smoothing width and is reported.
4. Fine stage: rare-word diagonals inside candidate regions
Inside a candidate region, every query k-mer q at query offset x that is present in the
index (and not above the df cutoff) is looked up, and for every stored occurrence at
chromosome position r in a window of the region it casts a vote on a diagonal:
same strand bit: d = r - x (forward)
opposite strand bit: d = r + x (reverse; the query start is d - qlen + k)
with weight ln(N/df(q)), into histograms binned at 20 bp. The histograms are smoothed with
a box whose half-width scales with the excursion of the diagonal expected under indels,
10 * sqrt(qlen * 0.65 * e) bp (at least one bin), where e is the query's error rate
inferred from its k-mer survival (section 3a) and 0.65 is the indel share of nanopore
errors. The peak gives the strand and, from the weight-averaged diagonal of the anchors
under the peak, the query's start to the base. The width matters, and in opposite
directions on simulated and real reads. A fixed ±200 bp costs two points at 1.5% error on
simulated 20 kb reads (the variant k-mers that separate one tandem copy from the next are
averaged away, in HSat3 and monomeric arrays especially) and a fixed ±20 bp costs six
points at 7% error (the diagonal drifts out of the window); three standard deviations of
the random walk is the optimum on simulated reads (79.8% within 10 kb). On real R10.4.1
ultra-long reads, whose indels are clustered (section 7), three sigma gives the lowest
agreement with minimap2 of any setting tried (75.4%) and ten sigma the highest (78.4%,
against 77.2% for the fixed 1% rule), for a cost of 0.6 points on simulated reads; ten is
the default. This is the FASTA diagonal
method (Lipman & Pearson 1985) with rarity weights and every occurrence of every retained
word; it scores no gaps and compares no bases.
Two quantities are computed from each region's histogram: the per-anchor peak, used for the position and its posterior within the region, and a segment peak (the same non-overlap rule as the coarse stage, applied along the query per diagonal bin), used to choose between regions. Per-anchor sums are what separate one HOR copy from the next -- collapsing them into segments cost 17 points of accuracy in active HOR arrays -- but on their own scale they would overrule the coarse stage wherever anchors are dense, which is exactly where the coarse stage should have the final word on which array or haplotype (section 5).
5. Choosing the region: composition updated by position
Composition and position have complementary strengths, measured on exact-truth reads from the diploid HG002 assembly. Inside HOR arrays, few k-mers are single-copy and the fine stage is starved, and composition chooses the right array and haplotype far more often (active HOR 89.5% within 10 kb against 70%). In monomeric alpha satellite, beta satellite and unique sequence, the arrangement of the words matters more than the words, and position chooses better (beta satellite 80% against 33%). Neither alone is best everywhere.
The region is therefore chosen by a Bayesian update of the coarse posterior: region i
with coarse mass m_i and segment fine peak F_i has posterior
P(i) = m_i exp(F_i / T_f) / ( sum_j m_j exp(F_j / T_f) + (1 - sum_j m_j) )
where the last term is the coarse mass outside every candidate, which receives no fine
update. The reported post is P(i*) times the within-region posterior mass of the fine
peak (within fine_tol = 2 kb of it); mapq is -10 log10(1 - post), capped at 60. When
no anchor is found in any region the coarse call stands and post is coarse_post.
6. Calibration
Both temperatures are fitted on reads whose origin is known exactly -- sketchy simulate
cuts reads from an assembly, records their coordinates in the header, and applies a stated
error model -- over a grid of (T, T_f) pairs (sketchy calibrate), with the decision
temperature held fixed. The fit set is 3,800 20 kb ONT-R10-like reads from the HG002 v1.1
diploid assembly. Temperatures scale linearly with query length from that 20 kb reference,
within a factor of eight: fitted separately, the best fine temperature is about four times
smaller on 5 kb reads and four times larger on 100 kb reads, because a wrong call inside a
near-identical repeat accumulates evidence along the whole read exactly as a right call
does. Scaling by length halves the calibration error on 5 kb reads (0.30 to 0.14). Accuracy is flat (79.8%) for any coarse T at or above 64, so the
coarse prior barely matters to the decision; the reported posterior is best balanced at
T = 256, T_f = 128 (expected calibration error 0.111, negative log-likelihood 0.85 per
read; the best ECE on the grid was 0.090 at 512/64 and the best NLL 0.68 at 512/256, and the
shipped pair sits between them). The decision temperature is 8. The evaluation sets are
different error models, read lengths and, for the real-read tests, different data
altogether; the write-up reports expected calibration error on each.
7. Read simulation
Per base, with probability rate, an error occurs and is a substitution, insertion or
deletion in the stated proportions; deletions inside homopolymers of length >= 4 are
hp_bias times more likely; an insertion extends with probability ins_ext. Presets:
asm (0.2%), hifi (0.3%, indel-dominated, homopolymer-biased), ont-r10 (1.5%),
ont-r9 (7%). Reads are drawn uniformly, or per annotation class from a BED, from either
strand. The model does not reproduce the context dependence of real nanopore errors; the
real-read benchmarks exist for that reason.
One consequence was measured directly (bench/drift.py, from minimap2's CIGAR strings):
the alignment diagonal of a real HG002 R10.4.1 ultra-long read wanders far more than the
model's. Real reads have a lower indel rate than the ont-r10 preset (0.0039 against
0.0103 per base) but a maximum excursion of the diagonal from its starting offset of
96 bp (median) and 433 bp (90th percentile) per read of median 77 kb, 5 to 13 times the
sqrt(L * r_indel) a random walk of independent indels predicts, against 1.4 to 1.9 times
for simulated reads; within any 20 kb of a real read the 90th-percentile excursion is
180 bp, against 32 bp for simulated 20 kb reads. Indels in real reads are clustered. This
is why the fine stage's smoothing width, which the simulations alone would set at the
random-walk scale, is set wider (section 4), and it is a caution for any benchmark that
tunes a mapper's gap tolerance on simulated nanopore reads.
8. Outputs
| column | meaning |
|---|---|
chrom start end strand |
the call; start is the leftmost reference coordinate |
mapq |
Phred-scaled post, capped at 60 |
post |
posterior of the call (region times position) |
coarse_start coarse_post |
the composition-only call and its mass within one window |
region_post |
posterior of the chosen region after the fine update |
llr |
coarse evidence at the peak, nats |
gap |
coarse evidence gap to the best start outside the guard region, nats |
cred_span cred_loci cred_frac |
the 90% credible set: total bp span, separate runs, fraction of the genome |
p_hat |
fraction of the query's comparable k-mer occurrences the best window explains |
seen_frac |
fraction of the query's k-mers the index has ever seen |
fine_ok fine_post fine_votes |
whether the fine stage anchored, its within-region mass, anchors under the peak |
candidates |
the credible runs with their masses |
9. Command lines behind every table
Indexes (final settings; --repeat-min-df 2 for a haploid reference, 3 for a diploid one):
sketchy index data/chm13v2.0.fa.gz -o data/chm13v2.0.skx --repeat-min-df 2
sketchy index data/hg002v1.1.fasta.gz -o data/hg002v1.1.skx
sketchy index data/hg002v1.1.pat.fa.gz -o data/hg002v1.1.pat.skx --repeat-min-df 2 --saturation-from data/hg002v1.1.skx
Exact-truth read sets from the diploid assembly, one per error model and length:
B=data/hg002v1.1.cenSatv2.0.bed; R=data/hg002v1.1.fasta.gz
sketchy simulate $R -o sim_L20k_ontr10.fa -n 1000 -l 20000 --model ont-r10 --bed $B --per-class 200 --seed 11
sketchy simulate $R -o sim_L20k_ontr9.fa -n 1000 -l 20000 --model ont-r9 --bed $B --per-class 200 --seed 12
sketchy simulate $R -o sim_L20k_hifi.fa -n 1000 -l 20000 --model hifi --bed $B --per-class 200 --seed 13
sketchy simulate $R -o sim_L100k_ontr10.fa -n 600 -l 100000 --model ont-r10 --bed $B --per-class 120 --seed 14
sketchy simulate $R -o sim_L5k_ontr10.fa -n 1000 -l 5000 --model ont-r10 --bed $B --per-class 200 --seed 15
Placement and competitors (5 threads each; Winnowmap2 needs a meryl repetitive-k-mer list
for the reference, WM_K15):
WM_K15=meryl/hg002v11_repetitive_k15.txt bench/run_methods.sh sim_L20k_ontr10.fa $R data/hg002v1.1.skx out/bench_L20k_ontr10 5 ont
bench/summarize_sim.py --queries sim_L20k_ontr10.fa --dir out/bench_L20k_ontr10 --tol 10000 --metric w10kb
bench/score_v2.py out/bench_L20k_ontr10/sketchy.tsv --tol 10000 --strip-hap # per-class table with ECE
sketchy calibrate -x data/hg002v1.1.skx sim_L20k_ontr10.fa --tol 10000 --fine-tol 10000 # temperature grid
bench/fig_v2.py classbars|reliability|errcdf|cost ... # figures
Cross-haplotype, maternal reads on the paternal index (the haplotype FASTAs are split from
the diploid assembly; data/hg002v1.1.pat.skx is built from the paternal one with
--repeat-min-df 2 as above):
gzip -dc $R | awk '/^>/{p=($0 ~ /_PATERNAL/)} p' | bgzip > data/hg002v1.1.pat.fa.gz
gzip -dc $R | awk '/^>/{p=($0 ~ /_MATERNAL/)} p' | bgzip > data/hg002v1.1.mat.fa.gz
grep _MATERNAL $B > censat_mat.bed
sketchy simulate data/hg002v1.1.mat.fa.gz -o mat_L20k_ontr10.fa -n 800 -l 20000 --model ont-r10 --bed censat_mat.bed --per-class 150 --seed 21
sketchy locate -x data/hg002v1.1.pat.skx mat_L20k_ontr10.fa -o sketchy_pat.tsv
minimap2 -cx map-ont --secondary=no data/hg002v1.1.pat.fa.gz mat_L20k_ontr10.fa > minimap2_pat.paf
bench/score_xhap.py --queries mat_L20k_ontr10.fa --bed $B --sketchy sketchy_pat.tsv --paf minimap2_pat.paf
Assembly segments on the acrocentric short arms (§4.11): the short arm of each acrocentric
chromosome of both HG002 haplotypes, telomere to the start of its active HOR array, is cut into
non-overlapping 100 kb tiles (tiles with fewer than 90% called bases, the unresolved rDNA gaps,
are dropped) and placed with every method on the diploid assembly, on the paternal haplotype
(maternal tiles) and on CHM13, with the aligners' asm20 presets. The reference standard
across genomes is the position lifted through a chromosome-scale alignment (blocks >= 20 kb at
mapq >= 30); the identity margin of a tile is the difference, in matched fraction, between the
best and the second-best CHM13 short arm among every alignment minimap2 reports for it.
D=out/v4/acro; B=data/hg002v1.1.cenSatv2.0.bed
bench/acro_tiles.py --fasta data/hg002v1.1.mat.fa.gz --bed $B --len 100000 --prefix acromat_ --out $D/tiles_mat_L100k.fa
bench/acro_tiles.py --fasta data/hg002v1.1.pat.fa.gz --bed $B --len 100000 --prefix acropat_ --out $D/tiles_pat_L100k.fa
cat $D/tiles_mat_L100k.fa $D/tiles_pat_L100k.fa > $D/tiles_L100k.fa
samtools faidx data/hg002v1.1.mat.fa.gz chr13_MATERNAL chr14_MATERNAL chr15_MATERNAL chr21_MATERNAL chr22_MATERNAL > $D/hg002_acro_chroms.fa
samtools faidx data/hg002v1.1.pat.fa.gz chr13_PATERNAL chr14_PATERNAL chr15_PATERNAL chr21_PATERNAL chr22_PATERNAL >> $D/hg002_acro_chroms.fa
samtools faidx data/chm13v2.0.fa.gz chr13:1-15547593 chr14:1-10092112 chr15:1-16678794 chr21:1-10962853 chr22:1-12788180 | sed -E 's/^>(chr[0-9]+):.*/>\1/' > $D/chm13_parms.fa
minimap2 -t 6 -cx asm20 --secondary=no data/chm13v2.0.fa.gz $D/hg002_acro_chroms.fa > $D/hg002acro_vs_chm13.paf # reference standard
minimap2 -t 2 -cx asm20 -N 20 -p 0.5 --secondary=yes $D/chm13_parms.fa $D/tiles_L100k.fa > $D/tiles_vs_chm13parms.paf # margins
samtools faidx data/hg002v1.1.pat.fa.gz chr13_PATERNAL chr14_PATERNAL chr15_PATERNAL chr21_PATERNAL chr22_PATERNAL > $D/hg002_acro_pat.fa; samtools faidx data/hg002v1.1.mat.fa.gz chr13_MATERNAL chr14_MATERNAL chr15_MATERNAL chr21_MATERNAL chr22_MATERNAL > $D/hg002_acro_mat.fa
minimap2 -t 8 -cx asm20 -N 20 -p 0.5 --secondary=yes $D/hg002_acro_pat.fa $D/tiles_mat_L100k.fa > $D/mat_vs_patacro.paf # nearest copy on the other haplotype
minimap2 -t 8 -cx asm20 -N 20 -p 0.5 --secondary=yes $D/hg002_acro_mat.fa $D/tiles_pat_L100k.fa > $D/pat_vs_matacro.paf
cat $D/mat_vs_patacro.paf $D/pat_vs_matacro.paf > $D/tiles_vs_otherhap.paf
minimap2 -t 4 -cx asm20 --secondary=no data/hg002v1.1.pat.fa.gz $D/hg002_acro_mat.fa > $D/matacro_vs_pat.paf # reference standard across haplotypes
for ref in data/hg002v1.1.pat.fa.gz data/chm13v2.0.fa.gz; do # Winnowmap2 k=19 lists, as for the diploid assembly
tools/winnowmap/meryl count k=19 output mdb $ref memory=8 threads=6 && tools/winnowmap/meryl print greater-than distinct=0.9998 mdb > $D/$(basename $ref .fa.gz)_repetitive_k19.txt; rm -rf mdb
done
WM_K19=out/v2/meryl/hg002v11_repetitive_k19.txt bench/run_methods.sh $D/tiles_L100k.fa data/hg002v1.1.fasta.gz data/hg002v1.1.skx $D/same 8 asm
WM_K19=$D/pat_repetitive_k19.txt bench/run_methods.sh $D/tiles_mat_L100k.fa data/hg002v1.1.pat.fa.gz data/hg002v1.1.pat.skx $D/xhap 8 asm
WM_K19=$D/chm13_repetitive_k19.txt bench/run_methods.sh $D/tiles_L100k.fa data/chm13v2.0.fa.gz data/chm13v2.0.skx $D/xind 8 asm
P="--placed sketchy=SET/sketchy.tsv --placed minimap2=SET/minimap2_asm20.paf --placed winnowmap2=SET/winnowmap2_asm20.paf --placed mashmap3=SET/mashmap3.paf"
bench/score_acro.py --setting same --queries $D/tiles_L100k.fa --target-bed $B --liftover out/v2/xhap/mat_vs_pat.paf --margin-same $D/tiles_vs_otherhap.paf ${P//SET/$D/same} --tiles-out $D/same_tiles.tsv --md $D/same.md
bench/score_acro.py --setting xhap --queries $D/tiles_mat_L100k.fa --target-bed $B --liftover $D/matacro_vs_pat.paf ${P//SET/$D/xhap} --tiles-out $D/xhap_tiles.tsv --md $D/xhap.md
bench/score_acro.py --setting xind --queries $D/tiles_L100k.fa --target-bed data/chm13v2.0_censat_v2.1.bed --liftover $D/hg002acro_vs_chm13.paf --margin $D/tiles_vs_chm13parms.paf ${P//SET/$D/xind} --tiles-out $D/xind_tiles.tsv --md $D/xind.md
bench/fig_acro.py --tiles $D/xind_tiles.tsv --out docs/v2/fig_acro_xind.png; bench/fig_acro.py --tiles $D/xhap_tiles.tsv --out docs/v2/fig_acro_xhap.png
v0.1's HPRC benchmarks (truth from minimap2 -cx asm20 -N 30 -p 0.5 against CHM13, run once and
kept in out/*_truth.tsv) are re-scored with bench/score_v2.py <placed.tsv> --truth <truth.tsv>
--tol 100000; the truth-free walk is bench/make_walk.py + bench/sweep.py run/score exactly as
in v0.1, with this version's locate in bench/sweep_methods.json.
Written by Claude Fable 5.1 (Anthropic) with @jlanej, September 2026; v0.1 methods by Claude Opus 5 and @jlanej.