Skip to main content

Fast Mixed Model Association for GWAS

Project description

CI PyPI Python 3.11+ JAX NumPy Hypothesis License: GPL-3.0 Buy Me a Coffee

JAMMA

Fast Mixed Model Association — A modern Python reimplementation of GEMMA for genome-wide association studies (GWAS).

  • GEMMA-compatible: Drop-in replacement with identical CLI flags and output formats
  • Numerical equivalence: Validated against GEMMA — 100% significance agreement, 100% effect direction agreement
  • Fast: Up to 4x faster than GEMMA 0.98.5 on LMM association (JAX backend), 2.6x faster end-to-end
  • Memory-safe: Pre-flight memory checks prevent OOM crashes before allocation
  • Cross-platform: Runs on Linux, macOS, and Windows — NumPy backend works everywhere, JAX backend adds GPU acceleration
  • Pure Python + optional C extension: NumPy + optional JAX stack; C extension with OpenMP provides 2-7x LMM speedup over JAX on CPU
  • Large-scale ready: Optional numpy-mkl ILP64 wheels (numpy 2.4.2) for >46k sample eigendecomposition

Installation

macOS (Intel or ARM)

pip install jamma          # NumPy backend
pip install jamma[jax]     # + JAX acceleration (ARM Mac only)

That's it. macOS Accelerate BLAS handles large matrices natively.

Linux / Windows

For small datasets (<46k samples), the standard install works:

pip install jamma          # NumPy backend
pip install jamma[jax]     # + JAX acceleration

For large-scale GWAS (>46k samples), install numpy-mkl first — standard numpy uses 32-bit BLAS integers which overflow at ~46k samples. Pre-built ILP64 wheels are available for Python 3.11–3.14:

NumPy backend only:

pip install numpy \
  --extra-index-url https://michael-denyer.github.io/numpy-mkl \
  --force-reinstall --upgrade
pip install jamma --no-deps
pip install psutil loguru threadpoolctl click progressbar2 bed-reader

With JAX acceleration:

pip install numpy \
  --extra-index-url https://michael-denyer.github.io/numpy-mkl \
  --force-reinstall --upgrade
pip install jamma[jax] --no-deps
pip install psutil loguru threadpoolctl click progressbar2 bed-reader \
  jax jaxlib jaxtyping

From Git (latest development version):

pip install numpy \
  --extra-index-url https://michael-denyer.github.io/numpy-mkl \
  --force-reinstall --upgrade
pip install git+https://github.com/michael-denyer/jamma.git --no-deps
pip install psutil loguru threadpoolctl click progressbar2 bed-reader

Why --no-deps? JAMMA depends on numpy>=2.0.0, so a normal pip install jamma will pull in standard numpy and overwrite the ILP64 build. --no-deps prevents this; you install the runtime dependencies manually instead.

See the User Guide for ILP64 verification steps.

Platform Support

Platform pip install jamma pip install jamma[jax] Notes
Linux x86_64 JAX (auto-included) Full support; ILP64 for >46k samples
ARM Mac (M1+) JAX (auto-included) Full support
Intel Mac NumPy only Not available JAX dropped Intel Mac support
Windows NumPy only JAX (CPU) Explicit opt-in via [jax] extra

JAX is auto-included on Linux and ARM Mac via platform markers. Force a specific backend with --backend numpy or --backend jax.

Quick Start

# Compute kinship matrix (centered relatedness)
jamma -gk 1 -bfile data/my_study -o output

# Run LMM association (Wald test)
jamma -lmm 1 -bfile data/my_study -k output/output.cXX.txt -o results

Output files match GEMMA format exactly:

  • output.cXX.txt — Kinship matrix
  • results.assoc.txt — Association results (chr, rs, ps, n_miss, allele1, allele0, af, beta, se, logl_H1, l_remle, p_wald)
  • results.log.txt — Run log

Python API

One-call GWAS (recommended)

from jamma import gwas

# Full pipeline: load data → kinship → eigendecomp → LMM → results
result = gwas("data/my_study", kinship_file="data/kinship.cXX.txt")
print(f"Tested {result.n_snps_tested} SNPs in {result.timing['total_s']:.1f}s")

# Compute kinship from scratch and save it
result = gwas("data/my_study", save_kinship=True, output_dir="output")

# With covariates and LRT test
result = gwas("data/my_study", kinship_file="k.txt", covariate_file="covars.txt", lmm_mode=2)

# LOCO analysis (leave-one-chromosome-out)
result = gwas("data/my_study", loco=True)

# Multi-phenotype with eigendecomp reuse
result = gwas("data/my_study", write_eigen=True, phenotype_column=1)
result = gwas("data/my_study", eigenvalue_file="output/result.eigenD.txt",
              eigenvector_file="output/result.eigenU.txt", phenotype_column=2)

# SNP filtering
result = gwas("data/my_study", kinship_file="k.txt", snps_file="snps.txt", hwe=0.001)

Low-level API (JAX backend)

import numpy as np

from jamma.io import load_plink_binary
from jamma.kinship import compute_centered_kinship
from jamma.lmm import run_lmm_association_streaming
from jamma.lmm.eigen import eigendecompose_kinship

# Load PLINK data and phenotypes
data = load_plink_binary("data/my_study")
phenotypes = np.loadtxt("data/my_study.pheno")  # loaded separately from .fam or phenotype file

# Compute kinship and eigendecompose (treat kinship as consumed after this)
kinship = compute_centered_kinship(data.genotypes)
eigenvalues, eigenvectors = eigendecompose_kinship(kinship)

# Run association (streaming from disk)
results, n_tested = run_lmm_association_streaming(
    bed_path="data/my_study",
    phenotypes=phenotypes,
    eigenvalues=eigenvalues,
    eigenvectors=eigenvectors,
    chunk_size=5000,
)

Low-level API (NumPy backend)

import numpy as np

from jamma.io import load_plink_binary
from jamma.kinship import compute_centered_kinship
from jamma.lmm import run_lmm_association_numpy
from jamma.lmm.eigen import eigendecompose_kinship

data = load_plink_binary("data/my_study")
phenotypes = np.loadtxt("data/my_study.pheno")
kinship = compute_centered_kinship(data.genotypes)
eigenvalues, eigenvectors = eigendecompose_kinship(kinship)

snp_info = [
    {"chr": str(data.chromosome[i]), "rs": data.sid[i],
     "pos": int(data.bp_position[i]), "a1": data.allele_1[i], "a0": data.allele_2[i]}
    for i in range(data.n_snps)
]

# Returns list[AssocResult] — write to disk via IncrementalAssocWriter
results = run_lmm_association_numpy(
    genotypes=data.genotypes,
    phenotypes=phenotypes,
    kinship=None,  # Not needed when eigenvalues/eigenvectors provided
    snp_info=snp_info,
    eigenvalues=eigenvalues,
    eigenvectors=eigenvectors,
    lmm_mode=1,
)

Memory Safety

Unlike GEMMA, JAMMA includes pre-flight memory checks that prevent out-of-memory crashes:

from jamma.core.memory import estimate_workflow_memory

# Check memory requirements BEFORE loading data
estimate = estimate_workflow_memory(n_samples=200_000, n_snps=95_000)
print(f"Peak memory: {estimate.total_gb:.1f}GB")
print(f"Available: {estimate.available_gb:.1f}GB")
print(f"Sufficient: {estimate.sufficient}")

Key features:

  • Pre-flight checks before large allocations (eigendecomposition, genotype loading)
  • RSS memory logging at workflow boundaries
  • Incremental result writing (no memory accumulation)
  • Safe chunk size defaults with hard caps

GEMMA will silently OOM and get killed by the OS. JAMMA fails fast with clear error messages.

Performance

Benchmark on mouse_hs1940 (1,940 samples × 12,226 SNPs), Apple M2, GEMMA 0.98.5:

Operation GEMMA 0.98.5 JAMMA (JAX) JAMMA (NumPy) JAX Speedup
Kinship (-gk 1) 2.1s 2.3s 1.7s ~1x
LMM (-lmm 1) 11.3s 2.8s 5.3s 4.0x
Total 13.4s 5.1s 7.0s 2.6x

Kinship is BLAS-bound (both use OpenBLAS/Accelerate matmul) so times are similar. The LMM speedup comes from JAMMA's batch-parallel SNP processing via jax.vmap.

Supported Features

Current

  • Kinship matrix computation — centered (-gk 1) and standardized (-gk 2)
  • Univariate LMM Wald test (-lmm 1)
  • Likelihood ratio test (-lmm 2)
  • Score test (-lmm 3)
  • All tests mode (-lmm 4)
  • LOCO kinship — leave-one-chromosome-out analysis (-loco)
  • Eigendecomposition reuse — multi-phenotype workflows (-d/-u/-eigen)
  • Phenotype column selection (-n)
  • SNP subset selection for association and kinship (-snps/-ksnps)
  • HWE QC filtering (-hwe)
  • Pre-computed kinship input (-k)
  • Covariate support (-c)
  • PLINK binary format (.bed/.bim/.fam) with input dimension validation
  • Large-scale streaming I/O (>100k samples via numpy-mkl ILP64 — numpy 2.4.2)
  • JAX acceleration (CPU/GPU) with automatic CPU device sharding
  • XLA profiling traces (--profile-dir) for TensorBoard/Perfetto
  • Lambda optimization bounds (-lmin/-lmax)
  • Individual weights for kinship (-widv)
  • Categorical covariates with one-hot encoding (-cat)
  • Pre-flight memory checks (fail-fast before OOM)
  • RSS memory logging at workflow boundaries
  • Incremental result writing
  • Optional C extension with OpenMP for NumPy LMM acceleration (auto-fallback to pure Python)

Planned

  • Multivariate LMM (mvLMM)

Architecture

JAMMA uses a dual-backend architecture: a JAX backend for GPU/multi-core acceleration and a NumPy backend that works everywhere with zero extra dependencies.

flowchart LR
    CLI["CLI / gwas()"] --> PIPE["PipelineRunner"]
    PIPE --> DET{"detect_backend()"}
    DET -->|"jax"| JAX["JAX Backend<br>JIT + vmap + sharding"]
    DET -->|"numpy"| NP["NumPy Backend<br>pure stdlib"]
    JAX --> RES["AssocResult"]
    NP --> RES

Both backends share the same core algorithms (likelihood.py, prepare_common.py) and produce identical results. Backend-specific files follow a naming convention: *_jax.py / *_numpy.py.

See Code Map for the full architecture diagram with source links.

Documentation

Requirements

  • Python 3.11+
  • NumPy 2.0+
  • JAX 0.8.0+ (optional, for GPU acceleration: pip install jamma[jax])

License

GPL-3.0 (same as GEMMA)

Project details


Release history Release notifications | RSS feed

Download files

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

Source Distribution

jamma-2.9.1.tar.gz (80.3 MB view details)

Uploaded Source

Built Distributions

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

jamma-2.9.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (345.2 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

jamma-2.9.1-cp313-cp313-macosx_11_0_arm64.whl (229.0 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

jamma-2.9.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (345.2 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

jamma-2.9.1-cp312-cp312-macosx_11_0_arm64.whl (229.0 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

jamma-2.9.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (345.0 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

jamma-2.9.1-cp311-cp311-macosx_11_0_arm64.whl (228.9 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

File details

Details for the file jamma-2.9.1.tar.gz.

File metadata

  • Download URL: jamma-2.9.1.tar.gz
  • Upload date:
  • Size: 80.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for jamma-2.9.1.tar.gz
Algorithm Hash digest
SHA256 29132ea99e9408a7919995fd8a02235bdf5893362bf8485a9686ff55cf777df9
MD5 9652741be9786d15252a7141cf1c70d0
BLAKE2b-256 570a7f806335dc969e9f48b33335cb1159619a26626d07e82ce1a0381797c320

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.1.tar.gz:

Publisher: build-wheels.yml on michael-denyer/jamma

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

File details

Details for the file jamma-2.9.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for jamma-2.9.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 19133b0e5a6492fc0f38bf9f2e98c8d74696402fe2031bd6704a248c4307367c
MD5 29323bb3ae20e718459cb98ea5028af3
BLAKE2b-256 05f0f408f8ae43b2c8b99febaac7d17b2ff1b1f4c80f3634bcbd6fc7df128977

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl:

Publisher: build-wheels.yml on michael-denyer/jamma

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

File details

Details for the file jamma-2.9.1-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for jamma-2.9.1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 754bc40da27dd0dbf4b36542b24f12c2cfcbdcf32133b3c3cf1659cdd200b5d7
MD5 85d82c94f21543b36333d881a613fd40
BLAKE2b-256 7210146eb703d17b454535e33ee4f7305af761cfab7c02d91b14e9f9b5c2413f

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.1-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: build-wheels.yml on michael-denyer/jamma

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

File details

Details for the file jamma-2.9.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for jamma-2.9.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 21d9322bd025056d76bf4ca287ffe5ccee9bc6708cec923b1b405f17284ad838
MD5 ace5fa21d1bd7d2b3a0fe8d1f4b49b49
BLAKE2b-256 aa4b0bee88cc69ebc7fbc1c5c0c92aebd6196ea63f34721b389e1f736577a457

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl:

Publisher: build-wheels.yml on michael-denyer/jamma

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

File details

Details for the file jamma-2.9.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for jamma-2.9.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ebb2160a6b74eac5f02518343dba394723fad6a00afe3aa0117876318f025dd6
MD5 2a8ad2f01c2e6100d41f4e0e85105bd7
BLAKE2b-256 bd66fb866a9d5480e895b19b701d8523506e767eb49322bf13ef9fd72656d389

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.1-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: build-wheels.yml on michael-denyer/jamma

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

File details

Details for the file jamma-2.9.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for jamma-2.9.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 af1033b4cf5f908647a769d53088d05ca26feb3e7b3b4f7916a55b60c95601d8
MD5 c4e7545e5446d68b8e7a2b1309141363
BLAKE2b-256 6feddc9dbaf30c38bd74d725e6f204b7d879ac0fd992a1d02bbe406459b26302

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl:

Publisher: build-wheels.yml on michael-denyer/jamma

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

File details

Details for the file jamma-2.9.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for jamma-2.9.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4695463b2c3f6e0fff13a03a7a60be7ad41cefeefeab6b74e6b55dfae4692f0b
MD5 c761c878a4ece1796e50625bb040b7da
BLAKE2b-256 59a3b0ec1e93e1c494693a417b1349ee3aa4ef12625bfe8c6bfb8a71af732422

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.1-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: build-wheels.yml on michael-denyer/jamma

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