Skip to main content

A Python interface to rammap, the Rust implementation of minimap2

Project description

🔗🐍🧬 rammappy Stars

PyO3 bindings and Python interface to rammap, the Rust implementation of minimap2.

Release PyPI ruff ty Code style: black

⚠️ Warning

This library is still a work-in-progress, and in an experimental stage, with API breaks very likely between minor versions.


🗺️ Overview

minimap2 is a widely used mapping tool for nucleotide sequences. rammap1 is a reimplementation of minimap2 in Rust, demonstrating perfect concordance while enabling performance optimizations for modern architectures. It maintains Rust's stronger memory safety constraints and provides a modular architecture.

rammappy is a Python module, implemented using the PyO3 framework, that provides bindings to rammap. It directly links to the rammap-core crate, which provides the following advantages:

  • zero-copy: Sequences are passed around as contiguous blocks of C-memory storing the byte sequences.
  • multithreaded: True parallel alignment inside a detached thread context that completely drops the Python GIL during batch processing.
  • lazy evaluation: Iterator-based result generation ensures massive mapping tasks don't blow up system memory.

🔧 Installing

rammappy can be installed directly from PyPI, which hosts some pre-built CPython wheels.

If you are building from source or working on development, we prefer using uv for environment and dependency management, along with just as a command runner:

just install

(This command wraps uv venv --allow-existing and uv pip install -e .)

💡 Examples

🧬 Parsing FASTA/FASTQ Files

rammappy includes a high-performance parsing module for FASTA and FASTQ files. It can automatically detect the file format and supports reading from disk, parsing directly from memory (bytes), or even streaming data in chunks!

Reading from a file

import rammappy

# FastxReader automatically detects FASTA or FASTQ and handles them efficiently
reader = rammappy.FastxReader("sequences.fa")
for record in reader:
    print(f"Name: {record.name}")
    print(f"Sequence: {record.sequence}")
    if record.quality:
        print(f"Quality: {record.quality}")

# Or use the convenience function to read all records at once
seqs = rammappy.read_fasta("sequences.fa")

Parsing from memory

# Parse bytes directly without touching the disk
data = b">seq1\nACGT\n>seq2\nGGGG\n"
seqs = rammappy.parse_fasta_bytes(data)

Streaming data For network streams or chunked processing, you can push chunks of bytes and process records as they become available:

streamer = rammappy.FastaStreamer(rna_to_dna=False)
streamer.push(b">seq1\nACGT\n>seq2\n")

# Process records that are complete
while (record := streamer.next_record()) is not None:
    print(f"Ready: {record[0]}")  # Prints seq1

# Push more data later
streamer.push(b"GGGG\n")
streamer.finalize()

while (record := streamer.next_record()) is not None:
    print(f"Ready: {record[0]}")  # Prints seq2

🧠 Building an Index In-Memory

A significant advantage of rammappy over mappy (the official minimap2 Python bindings) is the ability to build an Index directly from in-memory sequences. In mappy, reference sequences must generally be read from a FASTA file on disk. rammappy allows you to skip the disk I/O entirely:

import rammappy

# Define reference sequences
refs = [
    (b"chr1", b"ACGT" * 1000),
    (b"chr2", b"TGCA" * 1000)
]

# Build the index completely in-memory
index = rammappy.Index.build(refs, k=15, w=10)

# Optionally save it for later use
index.save("reference.mmi")

# You can also fetch sequences directly from the index!
print(index.seq("chr1", start=0, end=10))

🔨 Instantiating an Aligner

You can create an aligner by providing the built index and a preset configuration.

# Pass the in-memory index directly to the Aligner
aligner = rammappy.Aligner(index, preset=rammappy.Preset.Sr)

⚙️ Advanced Configuration (Options)

A core feature of rammappy is the ability to expose and modify the underlying mapping options on the fly. After creating an Aligner, you can access and mutate its options object to fine-tune seeding, chaining, or scoring behavior:

opts = aligner.options

# Modify seeding parameters
seeding = opts.seeding
seeding.min_mid_occ = 42
opts.seeding = seeding

# Modify scoring parameters
scoring = opts.scoring
scoring.match_score = 3
opts.scoring = scoring

# Apply the modified options back to the aligner
aligner.options = opts

🔬 Batch Alignment

Querying multiple sequences can be done via map_batch, which drops the GIL and runs in parallel using Rust's Rayon ecosystem:

queries = [
    (b"query1", b"ACGT" * 20),
    (b"query2", b"CGTA" * 20),
]

batch_results = aligner.map_batch(queries)

# Lazy-evaluate the iterator to pull hits
for i, mappings in enumerate(batch_results):
    first_hit = next(iter(mappings), None)
    if first_hit:
        print(f"Query {i+1} mapped to {first_hit.target_name.decode()} at {first_hit.target_start}")

📊 Exploring Sketchers

Direct access to seeding algorithms is available for customized genomic sketching:

sketcher = rammappy.MinimizerSketcher(kmer_len=15, window_len=10)
seed = sketcher.sketch(b"ACTG" * 50)[0]
print(f"Minimizer at position {seed.x} with y-value {seed.y}")

🏗️ Architecture & Implementation Philosophy

The overriding goal for this project was to establish extremely performant, zero-copy FFI bindings linking the Rust core API to Python. A focus was applied to maintain "bare metal Rust" speeds while providing a clean and "lazy" Python API.

graph TD
    A[Python Application] -->|Bytes & Queries| B[rammappy PyO3 FFI]
    B -->|GIL Dropped| C[Rayon Thread Pool]
    C -->|Zero-Copy Raw Pointers| D[rammap-core]
    D -->|Alignment Results| C
    C -->|Lazy Iterators| B
    B -->|Python Mapping Objects| A

Zero-Copy Evaluation Using bytes

Python strings (str) perform computationally expensive UTF-8 allocations and validation mechanisms. rammappy universally prefers Python byte-strings (bytes in Python, mapped to &[u8] in Rust). Data entering the alignment algorithm uses Bound<'py, PyBytes>, mapping directly to contiguous blocks of C-memory storing the sequences. Retrieving genomic strings mapping fields (e.g., CIGAR/MD/CS strings) exposes byte payloads directly without additional UTF-8 reallocations during FFI crossings.

In-Memory Indexing vs Disk I/O

Traditional bindings like mappy force users to write target sequences to a FASTA file before an index can be built and queried. rammappy decouples the Index from the Aligner, allowing indexes to be built dynamically from raw memory bytes inside Python, completely eliminating disk I/O bottlenecks for dynamic or programmatic reference generation.

Parallel Scaling and the Python GIL

Scaling genomic query alignments in parallel on multi-core systems mandates threading. However, the presence of the Python Global Interpreter Lock (GIL) poses problems, as normal PyO3 structures retain a lock on the main Python thread.

The Solution: For operations like Index::build, Index::load, and Aligner::map, the GIL is unconditionally released via py.detach(|| { ... }), allowing true multi-threading in Python while Rust performs the heavy computations.

For batch mapping (map_batch), we additionally wrap target queries in a custom struct RawQuery { name_ptr, seq_ptr, ... }. We implement unsafe impl Send for RawQuery and unsafe impl Sync for RawQuery, allowing pointer transmission across thread boundaries. After dropping the GIL with py.detach, rayon handles iterating and distributing alignments across all CPU cores. unsafe { std::slice::from_raw_parts } safely rebuilds the byte vectors in the isolated thread spaces, as the parent function's stack guarantees the memory allocation outlives the closure.

Lazy Materialization

Rather than computing alignment lists as heavy Vec<Mapping> aggregates and immediately converting every hit to a Python-native object (costing heavy FFI time), rammappy returns a MappingIterator. A MappingIterator acts as an opaque handle holding the vector of internal RustMapping entries. Only when a user invokes next(iterator) is the memory read and a single Python Mapping object initialized and surfaced over the FFI boundary.


👏 Acknowledgments

We would like to thank the original authors of rammap, Jeremy R. Wang and Heng Li, for their high-performance reimplementation of minimap2 in Rust.

📚 References

  1. Jeremy R. Wang and Heng Li. Memory-safe high-performance sequence mapping with rammap (2026). bioRxiv. 10.64898/2026.05.26.726289.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rammappy-0.1.2.tar.gz (94.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rammappy-0.1.2-cp310-abi3-win_amd64.whl (695.9 kB view details)

Uploaded CPython 3.10+Windows x86-64

rammappy-0.1.2-cp310-abi3-manylinux_2_34_x86_64.whl (932.2 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.34+ x86-64

rammappy-0.1.2-cp310-abi3-macosx_11_0_arm64.whl (772.9 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file rammappy-0.1.2.tar.gz.

File metadata

  • Download URL: rammappy-0.1.2.tar.gz
  • Upload date:
  • Size: 94.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rammappy-0.1.2.tar.gz
Algorithm Hash digest
SHA256 90bf022ef857fd6e2c85e1ff32adda7ccb8240271e32f6f285e930dcbb4c34f1
MD5 bef514c9e534a06cd6928ed4b3f04c0d
BLAKE2b-256 65f5d61d2386229ab97df2dba64e1cef741dbb848eee848687a97d9abc02cd7b

See more details on using hashes here.

Provenance

The following attestation bundles were made for rammappy-0.1.2.tar.gz:

Publisher: publish.yml on tomdstanton/rammappy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rammappy-0.1.2-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: rammappy-0.1.2-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 695.9 kB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rammappy-0.1.2-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 cf89ba6178dcae47ccc948e96d64461d211fc00914f3dc82b55cacda176e4400
MD5 20eb7f8396e058591f41e89437657654
BLAKE2b-256 0a8ab956236de24b0e01364be28c3068865e549606d0e38adcda6e8be88ee982

See more details on using hashes here.

Provenance

The following attestation bundles were made for rammappy-0.1.2-cp310-abi3-win_amd64.whl:

Publisher: publish.yml on tomdstanton/rammappy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rammappy-0.1.2-cp310-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rammappy-0.1.2-cp310-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 e63177e65fc2b74d13fe694d5986c76a7dbcef98c117d6b87a4032ffda20af6f
MD5 7e0870fc75bef4eb430e751953a9f3dd
BLAKE2b-256 79732375e6d180e2dace06f8eb93ebacb8d2283134fbbf997972650ab4f8bb0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for rammappy-0.1.2-cp310-abi3-manylinux_2_34_x86_64.whl:

Publisher: publish.yml on tomdstanton/rammappy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rammappy-0.1.2-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rammappy-0.1.2-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 893e895e39ab056ffdd0fa2d30daf100e73222a0fd14db5520721fc8ae41168b
MD5 c9dffd3111160a78fffef8c7d703dd4a
BLAKE2b-256 48ebf9f18a07601e52caf30c3197865332f958fbb5e9244db917d4a0f3add9a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for rammappy-0.1.2-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on tomdstanton/rammappy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page