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.1
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.1.tar.gz | 26.2 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| 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
|