Skip to content

Releases: sriram98v/haystackfm

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 22:20
3a10710

Added

  • Deduplication of identical input sequences in every index (FmIndex, BidirFmIndex,
    RlFmIndex, BidirRlFmIndex, MoveFmIndex, BMoveIndex; CPU and GPU builds). Input
    sequences with byte-identical bases (# layout included; headers may differ) form an
    equivalence class, and only the first member, its representative, is stored in the
    text. A sequence that is only a prefix or substring of another is not grouped. SeqIds
    keep their meaning (the position in the builder's input) for every sequence, stored or
    not, and sequence(id) returns any member's bases.
  • SeqClasses (seq_classes() on every index) plus equivalence_class(id),
    representative(id) and num_distinct_sequences() accessors.
  • Grouped query variants that report a match in a class once, on its representative:
    locate_grouped / locate_rows_grouped (FM, RL, Move), locate_interval_grouped
    (bidirectional indexes), BMoveIndex::locate_grouped, and find_smems_grouped /
    find_mems_grouped (BidirFmIndex, BidirRlFmIndex). BMoveIndex::count_cursor and
    count_fwd(&FwdInterval) (bidirectional indexes) count a cursor's occurrences over every
    member.

Changed

  • Breaking (results). A match in a sequence with identical copies is reported once per
    class member: locate, locate_rows, locate_interval, locate_fwd,
    FwdInterval::locate, BMoveIndex::locate, Mem::positions (with locate = true),
    FmIndex::locate_gpu and MemHit::positions (with the index's own seq_boundaries)
    list one hit per member, consecutively and by ascending id, in place of the
    representative's hit. Counts include every member: count, count_interval,
    Mem::match_count, MemHit::match_count and the GPU batch MEM counts (so
    match_count == positions.len() still holds for flat MEMs). Cursor size() and the
    count_*_in / count_wild_* helpers still count the stored text. Inputs without
    identical sequences give the same results as before.
  • Breaking. seq_boundaries() (now also on FmIndex), map_position,
    locate_positions(_many), text_len() and BMoveCursor::toehold describe the stored
    (deduplicated) text: boundaries are indexed by stored slot rather than SeqId, and raw
    positions lie in representatives only. find_smems_gpu / find_mems_gpu expand hits
    only when passed the index's own seq_boundaries(); with any other ref_boundaries
    table (e.g. one built in input order from the original sequences) positions are indices
    into that table, one per stored occurrence, not expanded.
  • Breaking (files). Format versions "HFM\x04", "HRL\x04" / "HRB\x04" and
    "HMV\x02" / "HBM\x02" store the sequence classes and the duplicate-row count weights.
    Older files still load (every sequence in a class of its own); blobs written by this
    version cannot be read by older builds. Loading now checks every format's sequence
    tables (header count, boundaries ending at the text length), so a corrupt older
    FmIndex file that used to load is now rejected.

Known limitations

  • The WASM bindings (FmIndexHandle) return the expanded locate / count results but do
    not yet expose the classes: no seq_classes, equivalence_class, representative,
    num_distinct_sequences or *_grouped variants from JavaScript.

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 22:02
1077d09

Added

  • # record delimiter: DnaSequence::from_str("ACGT#TTGA") (or DnaSequence::join)
    indexes several records (e.g. FASTA entries) as one logical sequence — one SeqId, one
    header, offsets counted across the joined string with each # taking one position. A
    # matches nothing: no hit spans it, SMEM/MEM extension stops at it as at $, and a #
    in a query matches nothing. Leading, trailing and doubled # are rejected.
    alphabet::decode_sequence renders extracted sequences with #.

Changed

  • Breaking (run-length files). Run-length format "HRL\x03" / "HRB\x03":
    extraction rows are stored per #-delimited piece. Version 2 files still load; blobs
    written by this version cannot be read by older builds. FORMAT_MAGIC /
    BIDIR_FORMAT_MAGIC now read "HRL\x03" / "HRB\x03".
  • Breaking. # is no longer an invalid character: encode_byte(b'#') /
    encode_char('#') return Some(SENTINEL) instead of None, and
    DnaSequence::from_str accepts an interior # (see Added) where it used to return
    InvalidCharacter.

Fixed

  • Locate on an index built from DnaSequence::from_encoded bases containing interior
    sentinels: every piece start is now force-sampled, so the LF walk never crosses one.
    Indexes already serialized from such text keep their old samples; rebuild them.

v0.6.9

Choose a tag to compare

@github-actions github-actions released this 13 Oct 04:39
82567a3

Performance

  • BidirFmIndex now runs the same SMEM/MEM sweeps as the run-length index: pruned MEM
    sweep, lockstep SMEM backward phase, a derived k-mer interval cache (at most 2^21
    intervals, built on construction and load, never serialized) and one batched locate per
    query.
  • BidirFmIndex sweep steps extend only the query symbol's compatible codes instead of
    ranking every symbol, answer an extension inside one 64-row occ block from that block
    alone, and filter left-maximal MEM rows a block at a time. On 200 Mbp at -O3:
    find_mems 4.4x, find_mems with locate 3.7x, find_smems 1.3x faster than 0.6.8.

Fixed

  • BidirFmIndex::from_bytes rejects forward and reverse halves that do not match.

v0.6.8

Choose a tag to compare

@github-actions github-actions released this 12 Oct 19:29
e6cc71e

First release with the run-length, move and b-move indexes. It covers 0.6.0 through 0.6.8; each patch version is one commit on the release-0.6.8 branch, squash-merged to main in #28.

0.6.8

Fixed

  • The run-length k-mer interval cache holds at most 2^21 intervals and stops at the last
    depth that fits: on a reference dense in ambiguity codes (or a crafted file, since loading
    builds the cache) it could otherwise allocate gigabytes.
  • RlFmIndex::locate_rows panics on rows outside the text instead of reading SA samples out
    of bounds; corrupted bidirectional halves yield no MEM rather than a wrapped count.

Performance

  • On a 200 Mbp strain collection (n/r 4.3, 2000 × 150 bp reads, min_len 19, -O3) the
    run-length bidirectional index finds SMEMs 1.6× and MEMs 5.3× faster than
    BidirFmIndex (3.5× with locate), with a 2.42 B/base serialized index vs 3.66.

0.6.7

Added

  • BidirRlFmIndex derives a k-mer interval cache (every ACGT k-mer up to depth
    min(8, log4 n), built by the sweeps' own extension so results are identical) that seeds
    the first, widest steps of each SMEM pivot and MEM start. Rebuilt at load, never
    serialized.

0.6.6

Changed

  • Run-length blocks store the next block's first run start in-line, so run searches and
    scans stay on one cache line; rank samples hold up to six symbols in one line.

0.6.5

Fixed

  • RlFmIndex::from_bytes rejects a zero-length first sequence and sequence end rows at or
    past the text length (a crafted file could panic during load), and
    BidirRlFmIndex::from_bytes rejects halves with different C arrays.
  • Run-length locate caps each LF walk at the sample rate (was the text length) and checks
    the position addition for overflow.

0.6.4

Changed

  • Run-length rank_all_pair reads a narrow range's ranks off one predecessor search, so
    BidirInterval::children_* cost one rank pass; the run-length sweeps take each step's
    compatible extension and drop set from it.
  • The run-length MEM sweep computes each drop's left-maximal occurrence count from
    coverages instead of left-extending, and builds a drop set only for accepted candidates.

0.6.3

Changed

  • Breaking (run-length files): RlBwt is laid out as 64-byte blocks of 8 runs (run
    starts, head nibbles and per-symbol rank samples on one cache line) behind a row→run
    bucket directory: a rank, LF step or extension reads two lines instead of a four-deep
    chain. Only the runs are serialized, as "HRL\x02" / "HRB\x02"; blocks and directory are
    rebuilt and the runs validated at load. "\x01" run-length files no longer load.
  • RlBwt::from_runs / from_bwt / from_occ / build_cpu drop their OccEncoding
    argument and RlFmIndexConfig::head_encoding is removed (there is no run-head table).
  • Run-length SMEM/MEM finding: MEM starts stop as soon as every occurrence is
    left-extendable and skip drop sets below min_len; SMEM prefixes are extended left in
    lockstep, reading right-maximality off the next-longer prefix and pruning equal-coverage
    prefixes; locate is batched per query with interleaved, prefetched LF walks. Results are
    identical to BidirFmIndex's.

Added

  • BwtRank::extend_ranks(c, lo, hi): one bidirectional extension step (both ranks and the
    in-range count of smaller symbols). The default keeps the occ table's behaviour; RlBwt
    walks the few runs of a narrow interval with one predecessor search and skips symbols
    absent from the BWT.

0.6.2

Added

  • BidirRlFmIndex::find_smems / find_mems: SMEM and MEM finding on the run-length index,
    identical to BidirFmIndex's. The sweep is shared through a crate-private trait over both
    index types; per-occurrence left-maximality now reads the forward BWT instead of the
    retained text.
  • RlFmIndex::sequence / sequence_by_header: extraction by LF-walk from one stored
    sentinel row per sequence (the text is not retained).
  • k-mer lookup table on RlFmIndex (RlFmIndexConfig::lookup_depth) seeding backward
    search; BidirRlFmIndex::lookup_interval / lookup_depth.
  • RlFmIndex::locate_positions_many; BidirRlFmIndex::extend_left_fwd, locate_fwd,
    count_interval; FwdInterval::extend_left generic over FmHalf.
  • GPU RlFmIndex::build / BidirRlFmIndex::build (feature gpu): an uncompressed GPU
    build, then converted.
  • examples/size_compare.rs: uncompressed vs run-length size, SMEM/MEM and extraction
    parity and timings on a FASTA.

Changed

  • "HRL\x01" / "HRB\x01" files also carry the sequence end rows and the lookup table.

0.6.1

Added

  • Move-structure indexes (haystackfm::move_index, haystackfm::move_table):
    • MoveTable: a permutation of [0, n) stored as 16-byte rows (start, image, image row,
      payload). step is one row read plus a short fast-forward. Optional Nishimoto–Tabei
      balancing bounds the fast-forward.
    • MoveFmIndex / MoveFmIndexConfig: an r-index on move tables.
      • count, locate, locate_positions.
      • Locate runs in O(r) space. SA samples at run boundaries give a toehold during
        search. Balanced φ / φ⁻¹ move tables with permuted-LCP samples enumerate the
        interval from a toehold anywhere inside it.
      • locate: false builds a count-only index.
    • BMoveIndex / BMoveCursor (b-move): forward and reverse LF move tables driving a
      cursor that carries both intervals, their rows and a toehold.
      • Operations: extend_*, children_*, count_*_in, count_wild_*, *_compatible,
        locate(&cursor), count.
      • BMoveCursor::interval() gives the equivalent BidirInterval.
    • Serialization under "HMV\x01" / "HBM\x01". Every table is validated on load.
    • benches/move_query.rs compares plain, run-length and move-structure indexes.

0.6.0

Added

  • Run-length indexes for repetitive collections, the substrate for the move / b-move
    structures:
    • RlBwt (haystackfm::rlbwt): a run-length BWT (run-head occ table, run starts with a
      bucket directory, per-symbol run-length prefix sums; ~9.6 bytes per run). Produced by
      RlBwt::from_runs, from_bwt, from_occ (convert an existing index) and build_cpu;
      inspected by runs, num_runs, run_ratio, get, to_bwt, heap_size_bytes.
    • RlFmIndex / RlFmIndexConfig (haystackfm::rl_fm_index): count, locate,
      locate_positions, locate_rows, map_position, header accessors; locate LF-walks to
      the sampled SA. sa_sample_rate = 0 builds a count-only index. From<&FmIndex>.
    • BidirRlFmIndex: the BidirInterval cursor (extend_*, children_*, count_*_in,
      count_wild_*, *_compatible), count, locate_interval. From<&BidirFmIndex>.
    • Serialization under their own magics, "HRL\x01" and "HRB\x01", validated in full at
      load (run structure, head table, C array, SA samples).
  • BwtRank: the rank/access trait the query code needs, implemented by OccTable and
    RlBwt. FmHalf (sealed): one half of a bidirectional index, as seen by the cursor.
  • Bwt::runs, FmIndex::occ, OccTable::encoding, OccTable::heap_size_bytes,
    SampledSuffixArray::heap_size_bytes.
  • benches/rl_query.rs: run-length vs plain count / locate / extend_right on seeded
    random and repetitive inputs.

Changed

  • BidirInterval::extend_*, children_*, count_*_in, count_wild_* and
    extend_*_compatible are generic over FmHalf (source-compatible: &FmIndex still
    works). Contraction still takes &FmIndex.

Fixed

  • FmIndex::from_bytes (and so BidirFmIndex::from_bytes) now checks the occ table's and
    SA samples' buffers against their declared lengths before returning. Their record reads
    are unchecked, so a corrupted or hand-crafted blob could previously read out of bounds
    on the first query; it is now a DeserializeError.
  • The BidirInterval docs had the extension formulae's directions swapped (right extension
    runs on the reverse index).

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 26 Sep 02:24
32e3feb

Added

  • AlphabetFns::new / from_compatible_fn / compatible / core / tag: an alphabet's
    matching semantics as data — one SymbolSet of compatible reference codes per query
    code plus the core symbol set and a tag. CArray::present_symbols.
  • LookupHit and LookupTable::MAX_VARIANTS_PER_ENTRY: a k-mer entry now exposes its
    exact interval and the intervals of its wildcard variants, and whether it is complete.
  • examples/gen_fixtures.rs writes the frozen-format fixtures under tests/fixtures/;
    version-2 fixtures (v2_*.hfm) added, including an ExactDna one.
  • The WGSL COMPAT / COMPAT_LEN tables in locate_search.wgsl and mem_find.wgsl are
    parsed out of the shader sources by a plain unit test and checked against IupacDna,
    instead of against a hand-copied Rust array.
  • serde_raw::u32_pairs blob codec for Vec<(u32, u32)>.
  • OccTable::rank_with_below(c, i) / rank_with_below_pair(c, lo, hi): rank(c, ·) and
    the class rank of every symbol below c from one block-record load per border — the
    two numbers a bidirectional extension step needs. OccTable::rank_all_pair(lo, hi):
    rank_all on both borders with both records prefetched first.
  • BidirInterval::contract_right / BidirFmIndex::contract_right: the inverse of
    extend_right (Pc → P), the mirror of contract_left run on the reverse half (select,
    LCP widening and class rank on the reverse index, forward interval recovered from the
    offset). Same cost and error behaviour as contract_left.
  • BidirInterval::contract_left / BidirFmIndex::contract_left: the inverse of
    extend_left (cP → P) without re-walking. One OccTable::select on the forward half
    gives ψ of the interval's first row, the forward LCP array widens that sub-range to the
    full interval of P (O(1) when every occurrence of P is preceded by c, O(log n) otherwise
    via block minima + a sparse table), and the existing class rank recovers the reverse
    interval. Works across sentinel-preceded occurrences and IUPAC reference codes.
    has_lcp() reports whether contraction is available.
  • LcpArray (src/lcp.rs): Kasai LCP over the SA rows stored capped at u16::MAX, with
    psv_below / nsv_below. Built during CPU construction while the full suffix array is
    resident; opt-in via FmIndexConfig::build_lcp (default false). A BidirFmIndex
    builds one per half (the forward one backs contract_left / parent_fwd, the reverse
    one contract_right), so opting in grows its resident and serialized size by ~2.7
    B/base per half. Default builds carry none: has_lcp() is false and the contraction
    / parent operations return LcpNotBuilt. BidirFmIndex::has_lcp() is true only when
    both halves carry one; use fwd().has_lcp() / rev().has_lcp() for a single direction.
  • OccTable::select(c, r): position of the r-th occurrence of c in the BWT, from the
    existing superblock/block/lane-mask layout plus a small hint array (rebuilt on load,
    not serialized).
  • Serialized indexes now start with a 4-byte format marker ("HFM" plus a version byte,
    currently 2; see Changed). Legacy blobs without it still load, with
    has_lcp() == false.
  • benches/serialize.rs and examples/serde_profile.rs measure to_bytes /
    from_bytes, the single-threaded startup cost a consumer pays before any query.
  • FmIndexError::LcpNotBuilt, PatternTooLong, InvalidContraction.
  • FwdInterval (BidirInterval::fwd()): a forward-only row range with extend_left
    (an LF step, valid on any sub-range of an interval), parent (the nearest suffix-tree
    ancestor P[..d'], d' = max(LCP[lo], LCP[hi]), widened over the LCP array) and
    locate. BidirFmIndex::extend_left_fwd / parent_fwd / locate_fwd and
    fwd() / rev() accessors; FmIndex::locate_rows(lo, hi). Together with
    contract_left these support sliding-window MEM sweeps whose ancestor MEMs are read off
    a parent walk instead of re-extended.
  • BidirFmIndex::lookup_interval(kmer) / lookup_depth(): seed a cursor for a
    lookup_depth-mer from the k-mer tables of both halves, skipping that many extensions.
  • SymbolSet: a 16-bit set of alphabet codes with EMPTY, ALL, BASES, WILDCARDS
    (codes 5..=15), NON_SENTINEL, single, below, from_codes, set algebra and
    iteration. AlphabetFns::compatible_set(q) returns the codes q matches under that
    alphabet as a SymbolSet.
  • OccTable::rank_set, rank_set_pair and rank_all: class ranks over the occurrence
    table. Every block record already holds all lanes, so counting "any symbol in this set"
    costs one block touch regardless of the set's size, and rank_all returns every symbol's
    rank at once.
  • Wildcard-aware bidirectional cursor operations, on both BidirInterval (taking the
    relevant &FmIndex half) and BidirFmIndex:
    • count_wild_right / count_wild_left: occurrences followed / preceded in the reference
      by an ambiguity code (N or a degenerate IUPAC symbol). Two occ-block touches, no text
      access, independent of how many wildcard codes exist — a cursor walk can ask at every
      step and fan out only when the answer is non-zero.
    • count_right_in / count_left_in: the same for an arbitrary SymbolSet, e.g.
      compatible_set(base).intersection(SymbolSet::WILDCARDS).
    • children_right / children_left: every child interval for all 16 codes from two
      rank_all calls, bit-identical to the per-code extend_* results; slot 0 is the
      sentinel child (occurrences at a reference end / start) and child sizes sum to the
      parent's size.
    • extend_right_compatible / extend_left_compatible: the compatible-symbol fan-out
      find_smems / find_mems use internally, now public, driven by the index's own
      alphabet; BidirFmIndex::compatible_set exposes that alphabet's match set.

Changed

  • Breaking (on-disk format). Serialized indexes are format version 3 ("HFM\x03"):
    the alphabet's matching tables are stored in full (35 bytes) instead of a tag, so an
    index built with a custom Alphabet loads back with the semantics it was built with,
    and the lookup table is stored in CSR form with wildcard variants (see Fixed). A
    built-in tag (0, 1) must match its tables and tags 2..=127 are reserved; both are
    rejected with DeserializeError. Version-2, version-1 and legacy blobs still load;
    their lookup table is converted in place when it is provably complete (pure-ACGT
    reference, or ExactDna) and rebuilt at load otherwise. FORMAT_MAGIC now reads
    "HFM\x03", FORMAT_MAGIC_V2 names the previous marker. Blobs written by this version
    cannot be read by older builds.
  • Breaking. AlphabetFns fields are private; construct with AlphabetFns::new or
    AlphabetFns::from_compatible_fn and read with compatible(q) / core() / tag().
    compatible_set remains as an alias. The fan-out order of extend_*_compatible,
    backward search and MEM extension is ascending code order (unchanged for the built-in
    alphabets, whose tables were already ascending).
  • Breaking. LookupTable::get returns Option<LookupHit> (exact slot plus variant
    intervals and a completeness flag) instead of Option<(u32, u32)>, and returns None
    for a k-mer of the wrong length rather than debug-asserting. Its build takes the
    AlphabetFns rather than a core-symbol slice. Memory on a pure-ACGT reference is about
    12 × 4^depth bytes (was 8) plus 8 bytes per stored wildcard variant.
  • Backward search and the locate resolve table intersect the query code's compatibility
    mask with the text's present symbols once per step, instead of walking a slice and
    checking each symbol's count: one loop iteration per present compatible code.
  • BidirInterval::extend_right / extend_left touch two occ block records per step
    instead of four: the LF rank and the below(c) class rank at each border now come from
    one fused load (rank_with_below_pair), with the below(c) lane mask precomputed per
    symbol at build/load time. children_right / children_left prefetch both borders
    before ranking. Results are bit-identical.
  • Breaking (on-disk format). Serialized indexes are format version 2 ("HFM\x02"):
    the occ table's checkpoints and block records, the SA samples, the LCP arrays and the
    text are written as length-prefixed little-endian byte blobs that from_bytes copies
    in bulk instead of decoding one element at a time through serde. Loading a 117 MB
    bidirectional index (10 Mbp, sa_sample_rate: 1, OneHot, no LCP) drops from ~630 ms
    to ~15 ms under the crate's release profile (~68 ms to ~10 ms at opt-level 3).
    Version-1 ("HFM\x01") and legacy blobs still load unchanged; any other "HFM" version
    is rejected with a clear DeserializeError. FORMAT_MAGIC now reads "HFM\x02" and
    FORMAT_MAGIC_V1 names the previous marker. Blobs written by this version cannot be
    read by older builds.
  • BidirFmIndex::to_bytes returns SerializeError instead of silently truncating the
    length prefix when the forward half exceeds 4 GiB.
  • Breaking. BidirInterval gains a len: u32 field (the matched pattern length,
    maintained by every extension and contraction); struct literals must supply it.
  • BidirFmIndex::build_cpu* now builds the reverse half with the configured
    lookup_depth (previously always 0) so lookup_interval can seed both halves; lookup
    memory doubles when the table is enabled.
  • Breaking. FmIndexConfig gains build_lcp: bool (default false); exhaustive
    struct literals must supply it. Set it to true only when the cursor contraction /
    parent operations are needed; CPU-built indexes then grow by ~2.7 bytes per base per
    half. GPU construction never builds the LCP.
  • BidirInterval::extend_right / extend_left compute the paired-interval offset with a
    single class rank per border instead of one scalar rank per smaller symbol. Results are
    unchanged; extending by a high IUPAC ...
Read more

v0.4.0

Choose a tag to compare

@sriram98v sriram98v released this 28 Jul 22:18
834a5fa

Breaking behavior change. The public API is unchanged — cargo semver-checks reports no
semver update required — but find_mems returns materially different results, so this takes
a minor bump under the pre-1.0 policy above rather than the patch bump an API-only check
would allow.

Fixed

  • Breaking. BidirFmIndex::find_mems now enumerates MEMs in the MUMmer / BWA sense:
    a query interval is reported when at least one of its occurrences is maximal in both
    directions at that occurrence. It previously required every occurrence to be
    maximal, so a single extendable occurrence in any one reference deleted the interval.
    Both the right-maximality test (the forward loop stopped only when the occurrence set went
    empty, discarding occurrences that dropped out mid-extension) and the left-maximality test
    (rejecting the interval if any occurrence was left-extendable) were affected.

    Consequences of the old behavior: at most one interval per query start position, and a
    result set that collapsed onto find_smems, making MEM-vs-SMEM comparisons through this
    API a guaranteed null result. In a database of near-identical references, per-reference
    match recovery was systematically sparse.

    For query = ACGTACGTAC against ACGTACGTAC and ACGTACT with min_len = 2, the result
    goes from {(0,10)} to {(0,2), (0,6), (0,10), (4,10), (8,10)}.

    find_smems is unaffected and unchanged: the containment-maximal MEMs under the new
    definition are exactly the SMEMs it already returned.

Changed

  • Breaking. Mem::match_count and Mem::positions from find_mems now cover only the
    maximal occurrences of a match, not every occurrence of the matched substring.
  • find_mems output size is now bounded by O(|query|²) rather than |query|; min_len is the
    only guard. Expect benches/mem_bench.rs and benches/mem_positions_bench.rs timings to
    move accordingly.

Known issues

  • find_mems_gpu still runs the previous whole-set algorithm and is not equivalent to
    find_mems. The shaders/mem_find.wgsl MODE_MEM port is tracked in KNOWN-ISSUES.md.

v0.3.0 — serve sequence bases by id

Choose a tag to compare

@sriram98v sriram98v released this 25 Jul 00:06
f0085f5

[0.3.0] - 2026-07-24

Breaking release: the index now retains the indexed text and can serve the bases of any
sequence, so callers no longer need to keep their own copy of every reference.

Added

  • FmIndex::sequence(SeqId) / BidirFmIndex::sequence(SeqId) — the bases of one indexed
    sequence as a borrowed slice, in O(1), with the trailing sentinel excluded. Returns
    alphabet codes (A = 1, C = 2, …), not ASCII; use decode_char to render them.
  • FmIndex::sequence_by_header(&str) / BidirFmIndex::sequence_by_header(&str) — the same
    by header, equivalent to seq_id(h).and_then(|id| self.sequence(id)).
  • encode_byte, encode_char and decode_char re-exported at the crate root, so callers
    can move queries into (and results out of) the alphabet's code space without reaching
    into the alphabet module.

Changed

  • Breaking. The serialized layout gains a text field, so indexes written by 0.2.0 and
    earlier are rejected by from_bytes and must be rebuilt.
  • The concatenated text is no longer dropped after BWT construction. This costs ~n bytes of
    resident and serialized size and forgoes a build-time peak-memory reduction, in exchange
    for random-access substrings. An FM-index can otherwise only recover text by LF-walking
    backwards one symbol at a time — far too slow for a caller rescoring a read against a
    candidate diagonal, which is the case this exists to serve.
  • Only the forward half of a BidirFmIndex retains text. The reverse half's copy is a
    redundant reversal that is never served, so it is released at build time, keeping the
    overhead at ~n rather than ~2n.

Upgrading

Indexes written by 0.2.0 and earlier are rejected by from_bytes and must be rebuilt.

Full changelog: v0.2.0...v0.3.0

webgpu-fmidx v0.3.0

Choose a tag to compare

@sriram98v sriram98v released this 09 Jun 23:46

What's New

Full IUPAC Nucleotide Alphabet Support (GPU)

  • Updated all GPU shaders (locate_search, locate_resolve, mem_find, mem_resolve) to support the full 16-symbol IUPAC alphabet (ALPHA=6 → 16)
  • Expanded C-array params in all GPU Params structs from 6 to 16 values with correct WGSL uniform alignment
  • Updated Rust driver structs in locate.rs, mem_find.rs, mem_resolve.rs to use [u32; 16] C-array bindings

Full IUPAC Support (CPU) — v0.2.0

  • All 16 IUPAC ambiguity codes (A, C, G, T, N, R, Y, S, W, K, M, B, D, H, V) supported in index, count, locate, SMEM, and MEM
  • IUPAC base-set intersection matching in CPU backward search and SMEM/MEM finding