gpgraph-v2
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.
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 ofO(N^2 * L). - Typed. Full type hints,
mypy --strictin CI. - Modern tooling.
uv+maturin+pyproject.toml. Releases viapython-semantic-release. OIDC-based PyPI publishing. - Consumes gpmap-v2. Speaks the locked
SCHEMA.mdcontract; readsbinary_packed,n_mutations, andphenotypes(not the removed v1fitnesses). - No Cython, no
setup.py, no.cblobs.
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-v2on PyPI; import path is stillgpgraph. - Construction: use
GenotypePhenotypeGraph.from_gpm(gpm, ...). v1 took gpm directly in the constructor (GenotypePhenotypeGraph(gpm)), which internally calledadd_gpm. - Reads gpmap-v2 columns: node attribute is
phenotypes, notfitnesses. __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_wrapperintrospection shim inpyplot/primitives.pyis gone. NetworkX is version-pinned to>=3.6. gpgraph.neighbor_functionsis renamedgpgraph.neighborsand the pairwiseget_neighborsreturns a NumPy(E, 2) int64array 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: ...-> patchfeat: ...-> minorfeat!: ...or aBREAKING 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)
| File | Size | Uploaded | |
|---|---|---|---|
| gpgraph_v2-1.1.2.tar.gz | 26.2 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| 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
|