Skip to main content

NetworkX-backed, Rust-accelerated graphs over genotype-phenotype maps.

Project description

gpgraph-v2

CI Documentation PyPI Python License Streamlit App

NetworkX-backed, Rust-accelerated graphs over genotype-phenotype maps.

gpgraph-v2 lifts a gpmap-v2 GenotypePhenotypeMap into a NetworkX DiGraph. Nodes carry the row attributes from the map; edges connect neighbors under a chosen distance metric (Hamming or codon). Fixation models (SSWM, ratio, Moran, McCandlish) populate an edge prob attribute for evolutionary trajectory analysis.

This is a clean-break rewrite of harmslab/gpgraph. Hot paths live in Rust via PyO3 + rayon; fixation models stay in vectorized numpy.

Genotype-phenotype graph laid out by Hamming distance, nodes colored by phenotype, edges weighted by SSWM fixation probability

Why v2

  • Fast. Neighbor detection runs in Rust with rayon parallelism. Biallelic cutoff-1 and cutoff-2 hit a bit-flip fast path: O(N * L^cutoff) instead of O(N^2 * L).
  • Typed. Full type hints, mypy --strict in CI.
  • Modern tooling. uv + maturin + pyproject.toml. Releases via python-semantic-release. OIDC-based PyPI publishing.
  • Consumes gpmap-v2. Speaks the locked SCHEMA.md contract; reads binary_packed, n_mutations, and phenotypes (not the removed v1 fitnesses).
  • No Cython, no setup.py, no .c blobs.

Install

pip install gpgraph-v2

Or with uv:

uv add gpgraph-v2

Plotting support is optional. For matplotlib:

pip install "gpgraph-v2[plot]"

Python 3.11+. Prebuilt wheels ship for Linux (x86_64, aarch64), macOS (x86_64, aarch64), and Windows (x64).

Quick start

from gpmap import GenotypePhenotypeMap
from gpgraph import GenotypePhenotypeGraph

gpm = GenotypePhenotypeMap(
    wildtype="AAA",
    genotypes=["AAA", "AAT", "ATA", "TAA", "ATT", "TAT", "TTA", "TTT"],
    phenotypes=[0.1, 0.2, 0.2, 0.6, 0.4, 0.6, 1.0, 1.1],
    stdeviations=[0.05] * 8,
)

G = GenotypePhenotypeGraph.from_gpm(gpm)
G.number_of_nodes()  # 8
G.number_of_edges()  # 24 directed (12 undirected pairs)

G.add_model(column="phenotypes", model="sswm")
G.edges[(0, 1)]["prob"]  # SSWM fixation probability from AAA -> AAT

Available fixation models: "sswm", "ratio", "moran", "mcclandish". Pass population_size=N as a keyword for the last two. Pass any f(fi, fj, **kw) -> float for a custom model.

Forward paths and flux

from gpgraph.paths import forward_paths_prob, paths_prob_to_edges_flux

G.add_model(column="phenotypes", model="sswm")
paths = forward_paths_prob(G, source=0, target=7)
flux = paths_prob_to_edges_flux(paths)

Plotting

from gpgraph.pyplot import draw_gpgraph, draw_paths

fig, ax = draw_gpgraph(G)             # Hamming-weight vertical layout
draw_paths(G, source=0, target=7)     # paths flux overlay

Benchmarks

vs v1 (harmslab/gpgraph 0.2.0)

Measured on Windows 11 against gpgraph==0.2.0. Full biallelic space (AT alphabet), timeit best-of-5. See benchmarks/vs_v1.py for scripts. v1 is capped at L=12 because its O(N^2 * L) neighbor search becomes impractical past that.

Build graph (neighbor detection)

L genotypes v1 (ms) v2 (ms) speedup
8 256 14.75 11.60 1.3x
10 1,024 59.93 47.05 1.3x
12 4,096 262.35 202.10 1.3x
14 16,384 (impractical) 919.88
16 65,536 (impractical) 3,901.40

Both versions are bottlenecked by NetworkX node/edge insertion at scale. The Rust neighbor detection eliminates the O(N^2) comparison but NetworkX graph mutation is the dominant cost. SSWM (below) shows the larger win.

Add SSWM fixation model

L genotypes v1 (ms) v2 (ms) speedup
8 256 73.09 13.57 5.4x
10 1,024 360.03 59.70 6.0x
12 4,096 1,756.46 264.68 6.6x
14 16,384 (impractical) 1,198.52
16 65,536 (impractical) 5,281.20

v1 iterates over edges one at a time in Python. v2 vectorizes fixation probability over all edges at once using NumPy, giving 5-7x speedup.

Rust kernels vs pure-Python pairwise reference

Windows 11, Ryzen, release build. Run via uv run pytest tests/benchmarks/ --benchmark-only.

problem bit-flip (Rust) pairwise (Rust) pairwise (Python)
N=256, L=6, cutoff=1 77 us 284 us 29.3 ms
N=1024, L=8, cutoff=1 167 us 1.2 ms (skipped, minutes)

At L=6 the Rust bit-flip path is ~380x faster than a naive Python loop. Past N = 1000 on biallelic data the bit-flip specialization pulls away from the rayon-parallel pairwise version by another ~7x.

Dispatch policy (neighbor kernels)

gpgraph.neighbors.get_neighbors chooses the fastest available implementation:

problem shape kernel
user-supplied f(g1, g2, cutoff=...) -> bool pure Python O(N^2)
Hamming, biallelic binary_packed, cutoff <= 2 Rust bit-flip (O(N * C(L, cutoff)))
Hamming, biallelic binary_packed, larger cutoff Rust rayon-parallel packed pairwise
Hamming, non-binary alphabet Rust rayon-parallel string pairwise
codon Rust rayon-parallel codon pairwise

See SCHEMA.md for the full node/edge contract.

Development

git clone https://github.com/lperezmo/gpgraph-v2
cd gpgraph-v2
uv sync
uv run maturin develop --release
uv run pytest
uv run ruff check python/gpgraph tests

After editing Rust:

uv run maturin develop --release && uv run pytest

Consuming from another local project

During co-development with gpmap-v2, point at the local source:

[tool.uv.sources]
gpgraph-v2 = { path = "/absolute/path/to/gpgraph-v2", editable = true }

[project]
dependencies = ["gpgraph-v2"]

Imports remain from gpgraph import GenotypePhenotypeGraph.

Migration from v1 (harmslab/gpgraph)

gpgraph-v2 is not wire-compatible with v1. Key differences:

  • Distribution name is gpgraph-v2 on PyPI; import path is still gpgraph.
  • Construction: use GenotypePhenotypeGraph.from_gpm(gpm, ...). v1 took gpm directly in the constructor (GenotypePhenotypeGraph(gpm)), which internally called add_gpm.
  • Reads gpmap-v2 columns: node attribute is phenotypes, not fitnesses.
  • __repr__ no longer renders a matplotlib figure as a side effect.
  • Matplotlib is an optional extra (gpgraph-v2[plot]); the core install is headless.
  • The _nx_wrapper introspection shim in pyplot/primitives.py is gone. NetworkX is version-pinned to >=3.6.
  • gpgraph.neighbor_functions is renamed gpgraph.neighbors and the pairwise get_neighbors returns a NumPy (E, 2) int64 array instead of a list of tuples.
  • Python 3.11+.

Releases

Releases are driven by python-semantic-release on merge to main. Commits follow Conventional Commits:

  • fix: ... -> patch
  • feat: ... -> minor
  • feat!: ... or a BREAKING CHANGE: footer -> major

CHANGELOG.md, version bumps, Git tags, GitHub Releases, wheel builds, and PyPI uploads all happen automatically.

License

MIT. See LICENSE.

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

gpgraph_v2-1.1.0.tar.gz (26.2 kB view details)

Uploaded Source

Built Distributions

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

gpgraph_v2-1.1.0-cp311-abi3-win_amd64.whl (227.2 kB view details)

Uploaded CPython 3.11+Windows x86-64

gpgraph_v2-1.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (398.8 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

gpgraph_v2-1.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (392.5 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

gpgraph_v2-1.1.0-cp311-abi3-macosx_11_0_arm64.whl (343.1 kB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

gpgraph_v2-1.1.0-cp311-abi3-macosx_10_12_x86_64.whl (349.7 kB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file gpgraph_v2-1.1.0.tar.gz.

File metadata

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

File hashes

Hashes for gpgraph_v2-1.1.0.tar.gz
Algorithm Hash digest
SHA256 7fe0a501bb087c82bd2900235c50a9f92e793e4edd6ad69cbf21337bf00ef411
MD5 b7a73ac7ca21ef23c0b490650cb08f6f
BLAKE2b-256 448d414ed27a100b8c0c3ad352227b5fcc6e65a96b996759fb9affe635324528

See more details on using hashes here.

File details

Details for the file gpgraph_v2-1.1.0-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: gpgraph_v2-1.1.0-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 227.2 kB
  • Tags: CPython 3.11+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gpgraph_v2-1.1.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 cd35e9ec1dec062bcaa4cbb9fd9ed64aa09e84358debc9e031c56c8f99a75ab5
MD5 8359371ea7f8f948af887386fc04faa8
BLAKE2b-256 f6322314307e0a7d075561f43d800590ad8b25489d0d03ca6b69096460571517

See more details on using hashes here.

File details

Details for the file gpgraph_v2-1.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gpgraph_v2-1.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 db00b20ac227f452a866067d1561d8e63c8659e1e33ae5026f83d90452a1e321
MD5 c31563319596c8858e3afa57fe870001
BLAKE2b-256 d97f789c9faa4f18e8e0e4c9af4d3d9cc01284e099e7ff459e3a883eb3c73140

See more details on using hashes here.

File details

Details for the file gpgraph_v2-1.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for gpgraph_v2-1.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 cfcbefbb714166a483e9eae9e068bc8e2763adcac902d1e732540c55b64eecf6
MD5 399330957040ed6fcc16a0973c885366
BLAKE2b-256 6a2eeacc68cfb43857bdde3212475e2b133b60f463c17a973ad7ce7f8ed7530c

See more details on using hashes here.

File details

Details for the file gpgraph_v2-1.1.0-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for gpgraph_v2-1.1.0-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 449f10a6f4a4355e713a8cdb50aa2360c10be5b3fe450f927f74b0584e0c2169
MD5 41f13a165cb9bdfd8a62e055551242d2
BLAKE2b-256 86ed54e4ee781d43426f9fa988044f12844664d0b6b25b42978d2ef673491b00

See more details on using hashes here.

File details

Details for the file gpgraph_v2-1.1.0-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for gpgraph_v2-1.1.0-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d67ef414e0210335c7a0d1c1b38a7fa7c61c096cbe78360d3f5fed3ce71ab671
MD5 0b3d3eed6ef696aa4ac4814d834df781
BLAKE2b-256 902417b4163a80a75f9077d7ec121da3bc0f9b5f19393be2cebfd912b6233eae

See more details on using hashes here.

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