fgumi compare CLI Reference

July 19, 2026 · View on GitHub

Developer tools for comparing BAM files and metrics outputs for testing and validation.

Requires: cargo build --features compare

Commands Overview

CommandDescriptionPrimary Use Case
fgumi compare bamsCompare two BAM filesValidate BAM output equality
fgumi compare metricsCompare two TSV metrics filesCompare fgumi/fgbio metrics

fgumi compare bams

Compare two BAM files for equality of core SAM fields and tag values.

Usage

fgumi compare bams <BAM1> <BAM2> [OPTIONS]

Required Arguments

OptionTypeDescription
<BAM1>PATHFirst BAM file
<BAM2>PATHSecond BAM file

Comparison Modes

ModeDescriptionUse Case
content (default)Pure record-by-record comparison of all fields, paired by RecordKey identity (order-sound: a reorder is reported as a difference, never masked)extract, zipper, correct, dedup, filter, clip, simplex, duplex, codec output (consensus depth tags compared exactly, clamped to fgbio parity at the source)
groupingStreaming molecule-join: matches molecules by an MI-invariant canonical id, then checks record membership, content (excluding MI), and duplex /A//B strand-partition equivalence per molecule. Order-independent at the molecule level; requires both inputs to already be grouped (same-MI reads consecutive within the file, as fgumi group/fgbio group output is) since it never re-sorts either input.Cross-tool group output, where MI values/molecule order may differ (see --command group)

Options

OptionTypeDefaultDescription
-c, --commandSTRING(none)Preset --mode/--ignore-order for a specific fgumi pipeline stage (see Command Presets below). For presets that support them, an explicit --mode/--ignore-order overrides the preset. The one exception is --command sort, which routes to the dedicated sort-verify engine and rejects both --mode and --ignore-order rather than letting them override.
--modeSTRINGcontentComparison mode: content or grouping
-m, --max-diffsINT10Maximum differences to report
-q, --quietFLAGfalseOnly exit code indicates result (0=equal, 1=different)
--ignore-orderFLAGfalseIgnore record order; valid only with --mode grouping (passing --ignore-order=true with any other mode is rejected). Grouping is already order-independent at the molecule level, so this flag is effectively a no-op there, retained for compatibility.
--buffer-sizeINT1000Initial buffer size for --ignore-order mode
-t, --threadsINT1Threads for BGZF decompression and comparison. content mode only; grouping mode routes to a single-threaded molecule-join engine and ignores this flag.
--batch-sizeINT10000Records per batch for parallel processing. content mode only; ignored by grouping mode.

Note: grouping mode (and --command group) is a streaming molecule-join — it never re-sorts either input, so there is no external-sort memory/temp-dir configuration to expose (no --sort-memory/--sort-tmp-dir). Both inputs must already be grouped (same-MI reads consecutive within the file), matching fgumi group/fgbio group output.

Command Presets (--command)

--command <stage> (short form -c) applies the canonical --mode/--ignore-order defaults for comparing BAM output from a specific fgumi pipeline stage, so you don't need to remember which mode/flags a given command's output requires. This is the primary, recommended way to compare a pipeline stage's output — especially for cross-tool comparisons (e.g. fgumi vs. fgbio) where MI values or record order may legitimately differ. For presets that support them, an explicit --mode or --ignore-order overrides the preset — the sole exception is --command sort, which routes to the dedicated sort-verify engine and rejects both options (passing either alongside it is an error).

Presets: extract, zipper, sort, correct, dedup, group, simplex, duplex, codec, filter, clip. See Recommended Settings by Command below for what each resolves to.

fgumi compare bams a.bam b.bam --command extract
fgumi compare bams a.bam b.bam --command group
fgumi compare bams a.bam b.bam --command simplex

Comparison Fields

The tool compares:

  • Core SAM fields: QNAME, FLAG, RNAME, POS, MAPQ, CIGAR, RNEXT, PNEXT, TLEN, SEQ, QUAL
  • Tag values: Order-independent comparison (tags may appear in different order)

Recommended --mode/--ignore-order settings for comparing each stage's output. The Preset? column says whether a --command <stage> preset exists: where it does, prefer --command <stage> over setting --mode/--ignore-order by hand (it applies exactly these defaults automatically). Stages without a preset have no --command <stage>; they use the default content mode, so no flags are needed.

fgumi CommandPreset?--mode--ignore-orderNotes
extract--command extractcontentfalseNo MI tags; deterministic
zipper--command zippercontentfalsePreserves MI tags unchanged
sort--command sort(dedicated sort-verify engine)n/aOrder is the payload: routes to the sort-verify engine, bypassing --mode/ContentPredicate entirely; explicit --mode/--ignore-order are rejected.
correct--command correctcontentfalseModifies RX tag only, not MI
dedup--command dedupcontentfalseDeterministic; exact content comparison
group--command groupgrouping (streaming molecule-join engine)trueCross-tool: compares via a streaming molecule-join, matching molecules across the two files by an MI-invariant canonical id (no re-sort — both inputs must already be grouped). Each matched pair is checked for record membership, content under ExactMinusMi (every field except MI), and duplex /A//B strand-partition equivalence, since MI values or molecule order may legitimately differ between tools.
simplex--command simplexcontentfalseExact content comparison (compares the cD/cM/cE depth tags exactly; fgumi clamps them to fgbio's Short ceiling at the source, so no saturation carve-out is needed)
duplex--command duplexcontentfalseExact content comparison (compares cD/cM/cE plus duplex aD/aM/bD/bM/aE/bE exactly)
codec--command codeccontentfalseExact content comparison, same as simplex/duplex
filter--command filtercontentfalseDrops failing reads and masks failing bases (rewriting SEQ/QUAL on the reads it keeps), but passes the MI/depth tags through unchanged; uses the same exact content comparison as consensus output, so a masking divergence is caught as a SEQ/QUAL difference
clip--command clipcontentfalsePost-consensus reads with overlapping/read-end clipping applied. Clipping rewrites CIGAR/SEQ/QUAL in the clipped span and, as a consequence, regenerates the alignment tags (NM/UQ/MD) and repairs mate-pair metadata; only the MI/depth tags pass through unchanged. Uses the same exact content comparison as filter/consensus output, which checks the regenerated alignment tags and repaired mate fields as well as SEQ/QUAL/CIGAR — sound because fgumi and fgbio clip regenerate them identically, so clipping-correctness diffs are caught
downsample❌ no preset (default content mode)contentfalseDeterministic with seed
review❌ no preset (default content mode)contentfalsePreserves MI tags

Examples

# Compare extract output (no MI tags)
fgumi compare bams extracted1.bam extracted2.bam --mode content

# Compare group output (cross-tool: streaming molecule-join)
fgumi compare bams grouped1.bam grouped2.bam --command group

# Compare consensus output (exact content comparison)
fgumi compare bams consensus1.bam consensus2.bam --command simplex
fgumi compare bams consensus1.bam consensus2.bam --command duplex
fgumi compare bams consensus1.bam consensus2.bam --command codec

# Compare with multi-threading for large files
fgumi compare bams large1.bam large2.bam --mode content --threads 8

# Quiet mode for CI pipelines
fgumi compare bams bam1.bam bam2.bam --quiet && echo "Files match"

Exit Codes

CodeMeaning
0Files are equal
1Files are different

fgumi compare metrics

Compare two TSV metrics files by outer-joining their data rows on a key column set, so rows may legitimately appear in different order (or one side may be missing a row entirely -- e.g. fgumi#498's dense-vs-sparse family-size rows) without the comparison cascading into spurious diffs on every following line. Numeric slack is confined to finite floats: they are first rounded to --precision decimal places, then accepted if they agree within --rel-tol or --abs-tol. The defaults are sized to absorb float representation differences (fgbio's DecimalFormat("0.######") vs fgumi's full precision), but any finite difference inside that window is accepted, not just a reformatting of the same number. Everything else -- the row set, integer/integer pairs, and strings -- is compared exactly; a mixed integer/float pair is tolerance-compared but keeps the integer's exact identity (see Comparison Behavior).

Usage

fgumi compare metrics <FILE1> <FILE2> [OPTIONS]

Required Arguments

OptionTypeDescription
<FILE1>PATHFirst TSV file
<FILE2>PATHSecond TSV file

Options

OptionTypeDefaultDescription
--precisionINT6Round floats to this many decimal places (-1 to disable)
--rel-tolFLOAT1e-9Relative tolerance for float comparison
--abs-tolFLOAT1e-9Absolute tolerance for float comparison
--key-columnsSTRING(first column)Comma-separated column name(s) or 0-based index(es) to join rows on, e.g. ab_size,ba_size
-m, --max-diffsINT20Maximum differences to display
-q, --quietFLAGfalseExit code is the only result signal (0=equal, 1=different): suppresses the stdout report and the command's own informational logging. Process-wide startup logging remains under RUST_LOG control (like fgbio's --log-level).
-v, --verboseFLAGfalsePrint success message when files match

Comparison Behavior

  • Both files' header rows must declare the same column set (order does not matter). A column-set difference is itself reported as DIFFER -- row values cannot be meaningfully aligned without it.
  • Data rows are joined on the key column(s) (default: the first column) as an outer join: a key present in both files has its remaining columns compared; a key present in only one file is reported individually (e.g. row 3 only in file1) instead of cascading into every following row.
  • Within a matched key: integer-to-integer values are compared exactly; floats are optionally rounded then compared with tolerance; a mixed integer/float pair (one file emits 1, the other 1.0) is compared with the same tolerance as float/float, with two soundness carve-outs: (a) if the float side is non-finite -- Infinity/-Infinity/NaN, exactly as fgbio serializes them -- the pair is not equal rather than tolerance-matched (an integer can never equal +/-inf or NaN); and (b) if the float side is itself an integer, the comparison is done in integer space, so a large integer (above 2532^{53}, where i64 as f64 loses precision) keeps its exact identity instead of collapsing onto a neighbouring float. strings are compared exactly.
  • The key column(s) must uniquely identify each row. Every metric is emitted from a map or histogram keyed by its dimensions, so a key that appears on more than one row means the chosen key set is under-specified. This is a hard error (not silently reconciled, which could mask a real difference): pass --key-columns naming the row's full identity (e.g. ab_size,ba_size for duplex_family_sizes).

Float Comparison

Finite floats are rounded to --precision decimal places first (-1 disables rounding), then considered equal if either condition is met:

  • |a - b| <= abs_tol
  • |a - b| <= rel_tol * max(|a|, |b|)

Non-finite floats bypass the tolerance formulas entirely and are compared by identity instead (fgbio serializes these as the literals NaN, Infinity, and -Infinity, which the parser accepts):

  • NaN equals NaN -- deliberately unlike IEEE 754, so that a metric which is legitimately NaN in both files (e.g. a ratio with a zero denominator) reports as equal rather than as a spurious diff.
  • An infinity equals only the same-sign infinity: Infinity == Infinity and -Infinity == -Infinity, but Infinity != -Infinity.
  • A non-finite value is never equal to a finite one, at any tolerance.

Examples

# Basic comparison with default precision
fgumi compare metrics file1.txt file2.txt

# Compare with 6 decimal precision
fgumi compare metrics file1.txt file2.txt --precision 6

# Looser tolerance for cross-platform comparison
fgumi compare metrics file1.txt file2.txt --rel-tol 1e-6 --abs-tol 1e-9

# Multi-column key, e.g. duplex_family_sizes keyed on (ab_size, ba_size)
fgumi compare metrics file1.txt file2.txt --key-columns ab_size,ba_size

# Compare a `review` command's <output>.txt (a headered TSV, one
# variant-site x consensus-read row per line): join on the site+read key so
# row-order differences are tolerated and a per-read consensus base/quality
# divergence is localized to the offending variant/read
fgumi compare metrics review1.txt review2.txt --key-columns chrom,pos,consensus_read

# Quiet mode for CI
fgumi compare metrics fgumi_metrics.txt fgbio_metrics.txt --quiet

Use Cases

  1. Comparing fgumi vs fgbio output: Different tools may produce slightly different floating-point representations, and rows may legitimately appear in a different order (the key-join tolerates reordering). A different row set — a key present in one file but not the other, or an extra/missing row — is not tolerated: it is a known fgbio-parity gap (e.g. fgumi#498) and is reported as DIFFER, never a successful comparison.
  2. Cross-platform validation: Float formatting may differ between platforms
  3. Regression testing: Verify metrics haven't changed between versions

Exit Codes

CodeMeaning
0Files are equal (within tolerance)
1Files are different

Workflow Examples

Validating a Full Pipeline

# Run pipeline twice with different implementations
fgumi extract ... --output extract1.bam
fgbio ExtractUmisFromBam ... --output extract2.bam

# Compare outputs
fgumi compare bams extract1.bam extract2.bam --mode content

# Compare downstream metrics
fgumi duplex-metrics --input duplex1.bam --output metrics1
fgumi duplex-metrics --input duplex2.bam --output metrics2
fgumi compare metrics metrics1.family_sizes.txt metrics2.family_sizes.txt

CI/CD Integration

#!/bin/bash
set -e

# Run tests and compare outputs
fgumi group --input test.bam --output actual.bam --strategy adjacency
fgumi compare bams expected.bam actual.bam --command group --quiet

echo "BAM comparison passed"