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.3.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.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (369.1 kB view details)

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

jamma-2.9.3-cp313-cp313-macosx_11_0_arm64.whl (250.2 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

jamma-2.9.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (369.2 kB view details)

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

jamma-2.9.3-cp312-cp312-macosx_11_0_arm64.whl (250.2 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

jamma-2.9.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (368.9 kB view details)

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

jamma-2.9.3-cp311-cp311-macosx_11_0_arm64.whl (250.2 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

File details

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

File metadata

  • Download URL: jamma-2.9.3.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.3.tar.gz
Algorithm Hash digest
SHA256 90db35e97afed2c7cc0652208f2f91daee1c3290392cbf7896eb74349f0b4664
MD5 e0582e6c8cb11ba95f8432d5fa768b68
BLAKE2b-256 fbd0ea91c75af5079ee0ac89478265dbd5bf667a869606b11bbc00b3024dda19

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.3.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.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for jamma-2.9.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9d412339210aeba76ccde69c705dd1af94c509da7b5c71aecd5132f9f47f0659
MD5 94f1d9c2980b5f0dea7bfa37125b17d6
BLAKE2b-256 8bc64753b46348b46e12d73fecceaaaf421667211dafae51299cec02cbabdc71

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.3-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.3-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for jamma-2.9.3-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c3427fd944acb0b4ca9553cfc116b6fbdcc9756ca989584a09b577787b59593c
MD5 81c40da50c715d7ba831c0c6ee8f48e3
BLAKE2b-256 68b014c7a5b2dd2b03ea718678115dbf08dabbbe69ebcb4a6db4af0be0a84cb7

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.3-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.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for jamma-2.9.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8b7bab58b1831e31f66a33bf9d9b76993bce232dc10591819ffd5b862c931efb
MD5 2de957d58814710c204caac5c467e643
BLAKE2b-256 e08130b877ebb13277948f6553fa94ce916669d93c703a423972990d8d4eb2bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.3-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.3-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for jamma-2.9.3-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 effcf31f333fa155dd4502c981fcc3ef7987256ea53f7e59ffef4b85350ea009
MD5 e4484dbe630024f9058f41e4a3ffb05a
BLAKE2b-256 3c7f1ec04d969fec426a04870746ab67fa966ee0ead2958f9d364f27be74f8a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.3-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.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for jamma-2.9.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 039496a55b06b69e81ea6bc0e1832f5360883e07af32df5529d1800efa366360
MD5 1b9365fb663b4b559c1fa494928f9dcb
BLAKE2b-256 05e3cf1900b4b360f7f776fbf6c2e62f72151cf0b8bcd2d88958fb3cd914a46e

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.3-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.3-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for jamma-2.9.3-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4b0e3669d1f6c0d404a016d1135f9ee22a6e7af5e4ad6616e48a1b4332244f3d
MD5 32fc3c033d013c2efb270132abfb1b80
BLAKE2b-256 5794d8babaddbc4744af958fd73e33cd72cd5717fc865d66d3a47dbfc7dcc916

See more details on using hashes here.

Provenance

The following attestation bundles were made for jamma-2.9.3-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