Skip to content

Repository files navigation

LRGE

check test DOI:10.1093/bioinformatics/btaf593

Long Read-based Genome size Estimation from overlaps

LRGE (pronounced "large") is a command line tool for estimating genome size from long read overlaps. It supports FASTQ, FASTA, and unaligned BAM, CRAM, and SAM formats. The tool is built on top of the liblrge Rust library, which is also available as a standalone library for use in other projects.

Michael B Hall, Chenxi Zhou, Lachlan J M Coin, Genome size estimation from long read overlaps, Bioinformatics, Volume 41, Issue 11, November 2025, btaf593, doi: 10.1093/bioinformatics/btaf593

Table of Contents

Installation

Precompiled binary

GitHub Downloads (all assets, all releases) GitHub Release

curl -sSL https://github.com/mbhall88/lrge/releases/latest/download/install.sh | sh
# or with wget
wget -nv -O - https://github.com/mbhall88/lrge/releases/latest/download/install.sh | sh

You can also pass options to the script like so

$ curl -sSL lrge.mbh.sh | sh -s -- --help
install.sh [option]

Fetch and install the latest version of lrge, if lrge is already
installed it will be updated to the latest version.

Options
        -V, --verbose
                Enable verbose output for the installer

        -f, -y, --force, --yes
                Skip the confirmation prompt during installation

        -p, --platform
                Override the platform identified by the installer [default: apple-darwin]

        -b, --bin-dir
                Override the bin installation directory [default: /usr/local/bin]

        -a, --arch
                Override the architecture identified by the installer [default: aarch64]

        -B, --base-url
                Override the base URL used for downloading releases [default: https://github.com/mbhall88/lrge/releases]

        -h, --help
                Display this help message

Conda

Conda Version Conda Platform Conda Downloads

conda install -c bioconda lrge

Cargo

Crates.io Version Crates.io Total Downloads

cargo install lrge

Container

Docker images are hosted on the GitHub Container registry.

Apptainer

Prerequisite: apptainer (previously Singularity)

$ URI="docker://ghcr.io/mbhall88/lrge:latest"
$ apptainer exec "$URI" lrge --help

The above will use the latest version. If you want to specify a version then use a tag like so.

$ VERSION="0.2.1"
$ URI="docker://ghcr.io/mbhall88/lrge:${VERSION}"

Docker

Prerequisite: docker

$ docker pull ghcr.io/mbhall88/lrge:latest
$ docker run ghcr.io/mbhall88/lrge:latest lrge --help

You can find all the available tags here.

Build from source

$ git clone https://github.com/mbhall88/lrge.git
$ cd lrge
$ cargo build --release
$ target/release/lrge -h

Usage

Important

The default values were calibrated from bacterial genomes, so you may need to adjust them if you are working with larger genomes. See below for more details.

Estimate the genome size of a set of Mycobacterium tuberculosis ONT reads (true genome size: 4.40 Mbp / 4405449 bp).

$ wget -O reads.fq.gz "ftp://ftp.sra.ebi.ac.uk/vol1/fastq/SRR283/049/SRR28370649/SRR28370649_1.fastq.gz"
$ lrge -t 8 reads.fq.gz
[2024-11-22T03:49:53Z INFO  lrge] Running two-set strategy with 10000 target reads and 5000 query reads
[2024-11-22T03:50:10Z INFO  lrge] Estimated genome size: 4.43 Mbp (IQR: 3.16 Mbp - 4.99 Mbp)
4426642
[2024-11-22T03:50:10Z INFO  lrge] Done!

The size estimate is printed to stdout, but you can also save it to a file with the -o flag.

$ lrge -t 8 reads.fq.gz -o size.txt
[2024-11-22T03:49:53Z INFO  lrge] Running two-set strategy with 10000 target reads and 5000 query reads
[2024-11-22T03:50:10Z INFO  lrge] Estimated genome size: 4.43 Mbp (IQR: 3.16 Mbp - 4.99 Mbp)
[2024-11-22T03:50:10Z INFO  lrge] Done!
$ cat size.txt
4426642

By default, LRGE uses the two-set strategy with 10,000 target reads (-T) and 5,000 query reads (-Q). You can use the all-vs-all strategy by specifying the number of reads to use with the -n flag.

In the paper, we ran LRGE on three eukaoryotic genomes: Arabidopsis thaliana (125 Mbp), Drosophila melanogaster (143 Mbp), and Saccharomyces cerevisiae (12 Mbp). We used 50,000 query and 100,000 target reads for A. thaliana and D. melanogaster, and 10,000 query and 20,000 target reads for S. cerevisiae. For H. sapiens we used 100,000 query and 2,000,000 targets reads. As genome size increases, more reads are needed to obtain sufficient overlaps for accurate estimation. While there’s no strict rule, we scaled the number of reads by the approximate order of magnitude difference between bacterial and eukaryotic genomes. LRGE’s defaults are calibrated for bacteria, so multiplying these by the expected genome size ratio is a good starting point.

Library

You can also use the liblrge library in your Rust projects. This allows you to estimate genome size within your own applications - without needing to call out to lrge. For more details on how to use the library, see the documentation or the source code.

Standard options

$ lrge -h
Genome size estimation from long read overlaps

Usage: lrge [OPTIONS] <INPUT>

Arguments:
  <INPUT>  Input FASTQ, FASTA, or unaligned BAM/CRAM/SAM file

Options:
  -o, --output <OUTPUT>      Output file for the estimate [default: -]
  -T, --target <INT>         Target number of reads to use (for two-set strategy; default) [default: 10000]
  -Q, --query <INT>          Query number of reads to use (for two-set strategy; default) [default: 5000]
  -n, --num <INT>            Number of reads to use (for all-vs-all strategy)
  -P, --platform <PLATFORM>  Sequencing platform of the reads [default: ont] [possible values: ont, pb]
      --normalize <MODE>     Control depth-aware read normalization [default: auto]
      --shortfall <MODE>     How to split an input too small to supply both read sets [scale, target] [default: scale]
  -F, --filter-contained     Exclude overlaps for internal matches 
  -t, --threads <INT>        Number of threads to use [default: 1]
  -C, --keep-temp            Don't clean up temporary files
  -D, --temp <DIR>           Temporary directory for storing intermediate files
  -s, --seed <INT>           Random seed to use - making the estimate repeatable
  -q, --quiet...             `-q` only show errors and warnings. `-qq` only show errors. `-qqq` shows nothing
  -v, --verbose...           `-v` show debug output. `-vv` show trace output
  -h, --help                 Print help (see more with '--help')
  -V, --version              Print version

Full usage

Estimate genome size of PacBio reads

$ lrge -P pb -t 8 reads.fq

Don't remove the intermidiate read and overlap files

$ lrge -C reads.fq

Use the all-vs-all strategy with 10,000 reads

$ lrge -n 10000 reads.fq

Fix the seed so that subsequent runs return the same size estimate

$ lrge -s 123 reads.fq

By default, we take the median of the finite estimates to get the final genome size estimate. If you want to include infinite estimates in the calculation

$ lrge -8 reads.fq

If you don't want the estimate to be rounded to the nearest integer 🤓

$ lrge --float-my-boat reads.fq

In the paper, we suggest using the 15th and 65th percentiles of the estimates to get a ~92% confidence interval. However, you can change these

$ lrge --q1 0.25 --q3 0.75 reads.fq

If you want to see the estimate for each read, turn on trace level logging

$ lrge -vv reads.fq

By default, the intermediate files are stored in a temporary directory. You can specify a different temporary directory

$ lrge -D ./mytemp/ reads.fq

If you have Illumina data, try GenomeScope2 or Mash (see alternatives for more details).


$ lrge --help
Genome size estimation from long read overlaps

Usage: lrge [OPTIONS] <INPUT>

Arguments:
  <INPUT>
          Input FASTQ, FASTA, or unaligned BAM/CRAM/SAM file

  Options:
  -o, --output <OUTPUT>
          Output file for the estimate

          [default: -]

  -T, --target <INT>
          Target number of reads to use (for two-set strategy; default)

          [default: 10000]

  -Q, --query <INT>
          Query number of reads to use (for two-set strategy; default)

          [default: 5000]

  -n, --num <INT>
          Number of reads to use (for all-vs-all strategy)

  -P, --platform <PLATFORM>
          Sequencing platform of the reads

          [default: ont]
          [possible values: ont, pb]

      --normalize <MODE>
          Control depth-aware read normalization

          [default: auto]

      --shortfall <MODE>
          How to split an input too small to supply both read sets [scale, target]

          [default: scale]

  -F, --filter-contained
          Exclude overlaps for internal matches
          
  -t, --threads <INT>
          Number of threads to use

          [default: 1]

  -C, --keep-temp
          Don't clean up temporary files

  -D, --temp <DIR>
          Temporary directory for storing intermediate files

  -s, --seed <INT>
          Random seed to use - making the estimate repeatable

  -8, --inf
          Take the estimate as the median of all estimates, *including infinite estimates*

  -f, --float-my-boat
          I neeeeeed that precision! Output the estimate as a floating point number

      --q1 <FLOAT>
          The lower quantile to use for the estimate

          [default: 0.15]

      --q3 <FLOAT>
          The upper quantile to use for the estimate

          [default: 0.65]

      --max-overhang-ratio <FLOAT>
          Maximum overhang size to alignment length ratio for internal overlap filtering

          Only meaningful alongside -F/--filter-contained, which this option requires.

          [default: 0.2]

      --use-min-ref
          Use the smaller Q/T dataset as minimap2 reference (for two-set strategy)

      --max-read-buffer <SIZE>
          Cap on the memory used to buffer selected reads when normalizing (e.g. 512M, 1.5G)
          
          Above this, lrge buffers read positions and reads the input one extra time. The reads selected for a given seed are the same either way.
          
          [default: 1G]

  -q, --quiet...
          `-q` only show errors and warnings. `-qq` only show errors. `-qqq` shows nothing

  -v, --verbose...
          `-v` show debug output. `-vv` show trace output

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version

Method

For a full description of the method, see the paper.

Uneven read depth

LRGE assumes that sampled reads represent genome positions uniformly. A short plasmid or other high-copy sequence can break this assumption by supplying most of the reads, even though it makes up little of the genome. The resulting estimate may collapse towards the size of that sequence.

The default --normalize auto mode checks minimizer depth before selecting reads. When it detects skew, it reduces the chance of retaining reads from high-depth sequence and draws both target and query reads from the normalized pool. LRGE reports the skew score and retained read count at WARN level so the depth-skewed input is not overlooked. Inputs without detected skew use the original sampling path unchanged.

Detection itself is cheap: it draws minimizers from about one read in a hundred and skips the rest. On an input too small for that to reach 500 reads it samples more, because below that the verdict starts to turn on which reads happened to be drawn rather than on the input. Building the full depth profile that normalization needs costs a second pass over the input, so LRGE only takes that pass once it has decided to normalize. That pass and the scoring of every read against the finished profile both use the thread count given to --threads. An input with no detected skew therefore costs little more than --normalize never.

Use --normalize always to normalize regardless of the skew verdict, or --normalize never to disable both detection and normalization. Forcing normalization still runs detection, because the depth a profile normalizes against is measured over the minimizers detection samples. A sample drawn from every read instead would be mostly sequencing error seen in one read only, and its median would be the noise floor of the sketch rather than the input's coverage depth.

Thin coverage does not hold normalization back. A shallow input gives a low median depth to normalize against, and at a median of one a read is kept or dropped on its own count coming out as two rather than three. Six skewed inputs with known genome sizes were subsampled until the median depth fell through three, two and one to see what that costs. Across the 46 resulting inputs, taking the median of three seeds each, auto landed at 0.64x to 0.99x of the true size and --normalize never at 0.004x to 0.24x; auto was nearer on every one of them. An even-depth input at the same median depth is still not called skewed, so shallow coverage on its own does not trigger normalization.

A wide reported interval means the per-read estimates disagree. Uneven depth is one possible cause; repeats, sparse overlaps, or too few sampled reads can also widen it.

Normalization holds the reads it selects in memory until sampling finishes, which for a large request on long reads can be more memory than a machine has. --max-read-buffer caps that buffer, 1 GB by default. The cap covers the selected reads and nothing else; scoring reads against the profile keeps a couple of megabytes of reads per thread in transit, which sits outside it. A request projected to need more than the cap is served by a path that buffers read positions instead of read sequences, then reads the input a second time to write them out: less memory, one extra pass. Both paths pick the same reads for a given seed, so the cap changes what a run costs while the estimate stays the same. The projection comes from the mean read length, so a run can still buffer past the cap; when it does, it says so and by how much.

Two-set strategy

The two-set strategy is the default method used by LRGE. It involves randomly selecting a two distinct subsets of reads from the input. One subset is deemed the target set ($T$) and the other the query set ($Q$). Each read $q_i$ in $Q$ is overlapped against $T$ and a genome size ($\textbf{GS}$) estimate is generated for that read ($\textbf{GS}_{T,q_i}$). The estimate is calculated based on the number of overlaps of $q_i$ with reads in $T$ ($\lvert \textbf{ov}(T \setminus q_i,q_i \rvert$), according to the formula:

$$\textbf{GS}_{T,q_i} \approx \lvert T \setminus q_i \rvert \frac{\ell_{q_i} + \overline{\ell}_{T \setminus q_i} - 2 \cdot \textbf{OT}}{\lvert \textbf{ov}(T \setminus q_i,q_i) \rvert}$$

where $\vert T \setminus q_i \vert$ is the total size of the target set minus the read $q_i$, $\ell_{q_i}$ is the length of read $q_i$, $\overline{\ell}_{T \setminus q_i}$ is the average length of reads in $T$ minus $q_i$, and $\textbf{OT}$ is the overlap threshold (minimum chain score in minimap2, which defaults to 100 for overlaps). See the paper for more formal/rigorous definitions.

Ultimately, the genome size estimate is the median of the finite estimates for each read in $Q$.

We use this strategy as the default as it is the most computationally efficient and the accuracy is comparable to the all-vs-all strategy. We suggest a smaller number of query reads than target reads, as this will speed things up and as we take the median of the estimates, the number of query reads (over a certain point) should not affect the accuracy of the estimate all that much.

That asymmetry is why an input with fewer reads than the two sets ask for is divided between them in the requested ratio, both sets shrinking together. An input of 7,473 reads asked for 10,000 target and 5,000 query gives 4,982 and 2,491. Up to and including v0.3.0 the whole shortfall came out of the target set, giving 2,473 and 5,000, so a request for twice as many target as query reads was served with half as many. To see what that costs, a pool of about 7,500 reads from three accessions with known genome sizes was divided five ways on three seeds each, from the split the old rule produces through to eight target reads per query read. Taking the median of the three seeds, the old rule landed at 0.010x, 0.156x and 0.209x of the true size on the three accessions, and the requested 2:1 at 0.081x, 0.268x and 0.247x. Those medians rise with the target's share at every step on all three accessions. Seven of the nine individual seed series do the same throughout; the two that do not each dip once and by little, 0.209x to 0.200x on SRR26715166 seed 42 and 0.385x to 0.361x on DRR213976 seed 4556. From 1:1 onward the reported interval also narrows against the estimate, on SRR12247681 from 90 times the estimate down to 1.9 times, so the higher estimates are the better determined ones as well; the one step it widens on is the first, out of the old rule's split.

Splitting further toward the target than the request asked for helped further still on those three accessions, all of which sit deep in the regime where the estimator is already failing. A wider sweep has since put a boundary on that: 27 accessions with known genome sizes, three pool sizes and both normalization modes, 2,328 runs in paper/corrections/issue63_split_calibration.tsv.

The split only matters when the pool is far below the request. With normalization on and a pool of about 7,500 reads, the spread across splits is three times the spread across seeds, and pushing from the requested 2:1 to 4:1 lands nearer the truth on 19 of the 27. At a pool of about 15,000, and on the whole input, that spread falls to between one and two times the seed spread and the same comparison comes out 13 of 23 and 11 of 23. Asking for 20,000 target reads against 5,000 query, rather than dividing 15,000 differently, moves the median estimate by 0.002.

So the defaults stay where they are, and the shortfall rule keeps the ratio it was asked for rather than leaning past it. What is left to gain is under half the seed-to-seed spread, it peaks at 4:1 and falls back at 8:1, and taking it would mean overriding an explicit -Q. Expected overlaps per query read, the quantity the estimator depends on, does not predict where the split matters either: its correlation with the gain from rebalancing is 0.28 where the pool is starved and under 0.13 everywhere else, so a floor on it would not be a better rule than a ratio.

Two limits on that. These accessions were collected for issue #29 because they estimate badly, so they are not a sample of ordinary inputs and the figures above are only meant as comparisons between splits within one accession. They also span 2.2 Mbp to 11.1 Mbp and are all bacterial, while the read counts in the paper move from 2:1 for bacteria to 20:1 for H. sapiens, so none of this says the split can be ignored at eukaryotic genome sizes.

Across the 17 benchmark accessions the change reaches only SRR26715166, the one input that cannot supply 15,000 reads. Its estimate moves from 0.828x to 0.912x of the true size under --normalize auto, and from 0.215x to 0.247x under --normalize never. Over 20 seeds the new rule is nearer the truth on 19 and 16 of them respectively. The other 16 accessions reproduce to the base pair. The estimate the paper reports for SRR26715166 is therefore out of date, and #36's rerun is what will replace it.

Pass --shortfall target to take the whole shortfall from the target set as before. Sizing the sets by number with -T and -Q is the more direct way to ask for a particular split, since the ratio is preserved whatever it is: a deliberately query-heavy request stays query-heavy. An input with fewer reads than the query request alone used to be an error, so LRGE refused to run on any input of 5,000 reads or fewer at the defaults; it is now divided like any other.

All-vs-all strategy

The all-vs-all strategy involves overlapping some random subset (-n) of reads in the input against each other. The genome size estimate for each read is calculated as above.

This strategy is generally more computationally expensive than the two-set strategy, but it can be more accurate. Though we did not find the difference to be statistically significant in our tests.

Results

We compared LRGE to three other methods: GenomeScope2, Mash, and Raven (see below for more info). We ran each method on 3370 read sets from PacBio or ONT data. Each of these samples is associated with a RefSeq assembly, so the true size was taken as the size of the RefSeq assembly. You can find the metadata for the samples here.

The full results are available in the paper and here. Here is a brief summary of how LRGE compares to other methods.

Results

This compares the absolute relative error as a percentage. The relative error ($\epsilon_{\text{rel}}$) is calculated as:

$$\epsilon_{\text{rel}} = \frac{\hat{G} - G}{G} \cdot 100$$

where $G$ is the true genome size, and $\hat{G}$ is the estimated genome size. For example, a $\epsilon_{\text{rel}}$ of 50% is out (higher or lower) by 50% of the true genome size. So if the true genome size is 1 Mbp, a $\epsilon_{\text{rel}}$ of 50% would be 1.5 Mbp or 0.5 Mbp.

The following figure shows the (non-absolute) relative error for the same methods to give an indication of which methods tend to over or underestimate.

Results

Benchmark

For the full details of the methods benchmarked, see the paper. However, here is a brief summary of the results.

Benchmark

The statistical annotations above the violins are coloured by the method which has the lowest mean value for the given metric.

Alternatives

The methods we compare against are:

GenomeScope2: to get estimates from GenomeScope2, you need to first generate a k-mer spectrum. We used KMC for this. You can find a Python script that takes reads, generates a k-mer spectrum, and estimates genome size in genomescope.py. The list of parameters used can also be found in the workflow config.

Mash: we used mash sketch on the reads, which prints out the estimated genome size in the logging output. You can find the options used in the workflow config.

Raven: Raven essentially just assembles the reads - REALLLLY fast 🚀

You can find the full details of how we compared methods in the workflow.

Citation

If you use LRGE in your research, please cite the following paper:

@article{hall_genome_2025,
	title = {Genome size estimation from long read overlaps},
	volume = {41},
	issn = {1367-4811},
	url = {https://doi.org/10.1093/bioinformatics/btaf593},
	doi = {10.1093/bioinformatics/btaf593},
	number = {11},
	journal = {Bioinformatics},
	author = {Hall, Michael B and Zhou, Chenxi and Coin, Lachlan J M},
	month = nov,
	year = {2025},
	pages = {btaf593},
}

About

Genome size estimation from long read overlaps

Topics

Resources

Stars

94 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages