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.1

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.1
File Size Uploaded
gpgraph_v2-1.1.1.tar.gz 26.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for gpgraph-v2 1.1.1
File
gpgraph_v2-1.1.1-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
gpgraph_v2-1.1.1-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.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details
gpgraph_v2-1.1.1-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details
gpgraph_v2-1.1.1-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.1.tar.gz

Download URL gpgraph_v2-1.1.1.tar.gz
Size 26.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ad284de468509fadfbf8b19224c20fd4523215c98d2c8b78518add94a8f82dfb
BLAKE2b-256 checksum
How to use checksums
e7eaf5afc35613e47822fcb90e0a81eb742e7eddb7c0b8120a0f1ad4c3976509
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.1-cp311-abi3-win_amd64.whl

Download URL gpgraph_v2-1.1.1-cp311-abi3-win_amd64.whl
Size 230.6 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
2fd09386a1a8f294e89ae2ecbec142af00c2cddd175a3f2b95e2384e991db3db
BLAKE2b-256 checksum
How to use checksums
fad68e1e779cf2eae4702226c4ce0f042ca7114967167ecd0f769b52aeee904f
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.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL gpgraph_v2-1.1.1-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
8564fb7ec3561a157931cc7fd76971d51ba299428b13d51d94979f93801c08e3
BLAKE2b-256 checksum
How to use checksums
0df869636c19bb9ba6f9af4ef3414c42bf5a12698a9a40bf92bb09740c33bb32
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.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL gpgraph_v2-1.1.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 395.7 kB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
06f720709f59c1eaef70d97d24074790de84ba561600d8d7a051a30ab2c64b3f
BLAKE2b-256 checksum
How to use checksums
4d1652b51d2a7111b039a1ee13a99fbe161c0b4b81dc678c7465874be20f162f
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.1-cp311-abi3-macosx_11_0_arm64.whl

Download URL gpgraph_v2-1.1.1-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
f46b5e0037419696da5709504dcbc80d603f2432b3178a923031c079961a2a6b
BLAKE2b-256 checksum
How to use checksums
1c9ac7785764b3f4dea5b4def8e78cd20fc2c97535b308b476d43d01e54be11c
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.1-cp311-abi3-macosx_10_12_x86_64.whl

Download URL gpgraph_v2-1.1.1-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
264b83db470e16e423e6f822a73628e98a768eda5cd5a56bf358ad3178ce9926
BLAKE2b-256 checksum
How to use checksums
88ad67bcfe9895bc263724e3851023bc732fe1e449650ab2ab5785677a709b9e
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

1.1.2

6 release files

This release

1.1.1 This release

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