Skip to main content

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.

Metadata

Release files for gpgraph-v2 1.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gpgraph-v2 1.1.2
File Size Uploaded
gpgraph_v2-1.1.2.tar.gz 26.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for gpgraph-v2 1.1.2
File
gpgraph_v2-1.1.2-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
gpgraph_v2-1.1.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 abi3 Linux glibc 2.17+ x86-64 Details
gpgraph_v2-1.1.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details
gpgraph_v2-1.1.2-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details
gpgraph_v2-1.1.2-cp311-abi3-macosx_10_12_x86_64.whl CPython 3.11 abi3 macOS 10.12+ x86-64 Details

Total release size: 1.8 MB

Release files / gpgraph_v2-1.1.2.tar.gz

Download URL gpgraph_v2-1.1.2.tar.gz
Size 26.2 kB
Tags Source
SHA-256 checksum
How to use checksums
4ed309885fb403c0d9caa2452d29b514e16fe83c06df3f24b041baa826669311
BLAKE2b-256 checksum
How to use checksums
fdc1e615736632effc031a76772e581caee95eb4735906e7a0f857a2192cba1e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / gpgraph_v2-1.1.2-cp311-abi3-win_amd64.whl

Download URL gpgraph_v2-1.1.2-cp311-abi3-win_amd64.whl
Size 230.6 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
d080bd6f34fb45923f9fd1d3878ac0270bb1955b665313db08796307bf407fd6
BLAKE2b-256 checksum
How to use checksums
e5b5f84f137cfabd83ed7dbd1813db7f3b8684896f0cf27861e16977cdd9a251
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / gpgraph_v2-1.1.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL gpgraph_v2-1.1.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 403.9 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
f792c99ca2b6dd5d9d1a24d15c474586f6dc45208d2465488de366cb4a561cb2
BLAKE2b-256 checksum
How to use checksums
50e530c04a9a6e7ccc0cd870c12618e7522d01662f4c8ecfa843c0ce226a2ca5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / gpgraph_v2-1.1.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL gpgraph_v2-1.1.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 395.6 kB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
fdebde943aa8f42a82d9deaab0881704fae3e57af7227fb8d3c6fc050b263294
BLAKE2b-256 checksum
How to use checksums
54832ea12c1f2cdc499bdb02d52ffd4fc263c7dc82aae17cb6bd85ffcd946996
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / gpgraph_v2-1.1.2-cp311-abi3-macosx_11_0_arm64.whl

Download URL gpgraph_v2-1.1.2-cp311-abi3-macosx_11_0_arm64.whl
Size 348.0 kB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
f90c16cdd11b90a843cf52da47bada1568b1decd5f63fabc105089f31d24208c
BLAKE2b-256 checksum
How to use checksums
78d96a331856fd4dc5961a5175d8a9845fd186f73cbabec2c9e2056afbaaccd9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / gpgraph_v2-1.1.2-cp311-abi3-macosx_10_12_x86_64.whl

Download URL gpgraph_v2-1.1.2-cp311-abi3-macosx_10_12_x86_64.whl
Size 350.0 kB
Tags CPython 3.11 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
0508d6a131b278dade5e89ff0640d44a0378c321eb25ed518945765e161fd64a
BLAKE2b-256 checksum
How to use checksums
c217d77a8b202b9803466a3c58dba2615f06793dd4acf12c1167c44869b6178b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

1.1.2 This release

6 release files

1.1.1

6 release files

1.1.0

6 release files

1.0.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page