HypergraphZ

May 17, 2026 · View on GitHub

GitHub Actions Workflow Status PyPI

A fast, directed hypergraph library written in Zig, with Python bindings included.

In a regular graph an edge connects exactly two nodes. A hyperedge connects any number of vertices at once — making hypergraphs a natural fit wherever relationships involve more than two parties:

DomainVerticesHyperedges
Academiaresearcherspapers (all co-authors at once)
Biologyproteinscomplexes, metabolic pathways
Chemistrymoleculesreactions (multiple reactants → products)
Financeaccountstransactions (split payments, settlements)
Softwarepackagescommits, dependency sets
Networksdevicesmulticast groups, subnets
Socialpeoplegroup chats, events, committees
Knowledgeconceptsontology relations, semantic clusters
Logisticslocationsroutes, shared shipments
ML / AIfeaturesco-occurrence sets, hypergraph neural nets

HypergraphZ adds directed semantics on top: consecutive vertex pairs within a hyperedge define a direction, enabling shortest-path queries, topological sort, and reachability over the same structure.

Highlights

  • Hyperedges can span zero, one, or many vertices — including self-loops and duplicates
  • Full algorithm suite: BFS/DFS, shortest path, PageRank, centrality, cut vertices, connected components, topological sort, transitive closure
  • Structural operations: dual, k-skeleton, line graph, star/clique expansion, incidence matrix, Laplacian
  • Python wheels ship a precompiled Zig core — no compiler needed at install time
  • Bulk insertion API (create_vertices, create_hyperedges) cuts Python FFI overhead ~10×

For Python users

How it works

The Python package is a thin binding over the same Zig core used by the native library — there is no Python reimplementation of the graph logic.

┌─────────────────────────────┐
│  Python (hypergraphz)       │  high-level API — Hypergraph, VertexQuery, …
├─────────────────────────────┤
│  ctypes                     │  FFI bridge — no C extension, no compilation step for users
├─────────────────────────────┤
│  C ABI (hgz_c_api.zig)      │  flat exported functions — hgz_add_vertex, hgz_find_shortest_path, …
├─────────────────────────────┤
│  HypergraphZ (Zig core)     │  all algorithms, memory management, type-safe generics
└─────────────────────────────┘

The shared library (libhypergraphz.so / .dylib / .dll) is compiled once and shipped inside the wheel. Data crosses the boundary as JSON (vertex and hyperedge payloads) and as plain integer ID arrays (all structural operations). For bulk insertion, create_vertices and create_hyperedges serialise an entire payload list in one call, reducing FFI crossings from N to 1 — see Performance below.

Installation

pip install hypergraphz
# or with uv
uv add hypergraphz

Pre-built wheels are available for Linux (x86_64, aarch64), macOS (x86_64, arm64), and Windows (x86_64).

Quick start

from hypergraphz import Hypergraph

g = Hypergraph()

# Add vertices and hyperedges
alice = g.create_vertex({"name": "alice", "age": 30})
bob   = g.create_vertex({"name": "bob",   "age": 25})
carol = g.create_vertex({"name": "carol", "age": 35})

collab = g.create_hyperedge({"type": "project"})
g.append_vertices(collab, [alice, bob, carol])

# Build the reverse index before querying
g.build()

# Shortest path
path = g.find_shortest_path(alice, carol)

# Fluent query builders
active = (
    g.vertices()
     .where(lambda d: d["age"] > 28)
     .data()
)

# Structural algorithms
scores, _, _ = g.compute_page_rank()
centrality    = g.compute_centrality()
components    = g.get_connected_components()

# Sub-graph operations return independent Hypergraph objects
sub  = g.get_vertex_induced_subhypergraph([alice, bob])
dual = g.get_dual()

API summary

CategoryMethods
Lifecyclebuild(), clear(), save(), load(), clone()
Verticescreate_vertex(), get_vertex(), update_vertex(), delete_vertex(), count_vertices(), get_all_vertex_ids()
Hyperedgescreate_hyperedge(), get_hyperedge(), update_hyperedge(), delete_hyperedge(), count_hyperedges(), get_all_hyperedge_ids()
Relationsappend_vertices(), prepend_vertices(), insert_vertex(), insert_vertices(), delete_vertex_from_hyperedge(), delete_vertex_at_index()
Degreeget_vertex_indegree(), get_vertex_outdegree()
Queriesget_hyperedge_vertices(), get_vertex_hyperedges(), get_vertex_neighborhood(), get_intersections(), get_hyperedges_connecting(), get_endpoints(), get_orphan_vertices(), get_orphan_hyperedges()
Booleanis_connected(), is_reachable(), has_cycle(), is_k_uniform()
Traversalfind_shortest_path(), find_all_paths(), bfs(), dfs(), random_walk()
Algorithmsget_connected_components(), topological_sort(), find_cut_vertices(), compute_centrality(), compute_page_rank(), get_inclusions(), get_nestedness_profile()
Matricesto_incidence_matrix(), to_incidence_matrix_coo(), to_laplacian()
Sub-graphsget_dual(), get_k_skeleton(), get_vertex_induced_subhypergraph(), get_edge_induced_subhypergraph(), get_core(), expand_to_graph(), expand_to_star(), get_line_graph(), get_transitive_closure()
Fluent buildersvertices()VertexQuery, hyperedges()HyperedgeQuery

Exceptions

All errors map to typed exceptions from hypergraphz:

ExceptionWhen raised
NotBuiltErrorQuery requires build() first
VertexNotFoundErrorVertex ID does not exist
HyperedgeNotFoundErrorHyperedge ID does not exist
CycleDetectedErrorOperation requires a DAG
NoPathErrorNo directed path exists
IndexOutOfBoundsErrorInsertion index out of range
NotEnoughVerticesErrorOperation needs more vertices

Performance

Benchmarks run with pytest-benchmark on an Intel Core i9-13900H, 64 GB RAM, CachyOS Linux (kernel 7.0.5), ReleaseFast shared library, Python 3.14.

Insertion — 1,000 hyperedges × 1,000 vertices

APIMeanvs atomic
`create_vertex$ \text{per} \text{vertex} (\text{atomic})~4{,}095 \text{ms}1 \times
$create_vertexper vertex +append_vertices` (batch append)~2,554 ms1.6×
create_hyperedges + create_vertices (bulk, single FFI call each)~415 ms~10×

The dominant cost in the atomic and batch cases is ctypes call overhead (~1–2 µs per crossing). create_hyperedges and create_vertices each replace a per-item loop with a single FFI call, serialising the entire payload list as one JSON array.

Queries (chain / shared-vertex graphs, after build())

OperationMean
find_shortest_path — chain of 1,000 vertices~148 µs
find_cut_vertices — chain of 1,000 vertices~351 µs
get_vertex_indegree × 100 — each vertex in 1,000 hyperedges~4.7 ms

For Zig users

Zig version

HypergraphZ currently requires Zig 0.17.0-dev.242+5d55999d2.

Usage

Add hypergraphz as a dependency to your build.zig.zon:

zig fetch --save https://github.com/yamafaktory/hypergraphz/archive/v0.1.0.tar.gz

Add hypergraphz as a dependency to your build.zig:

const hypergraphz = b.dependency("hypergraphz", .{
    .target = target,
    .optimize = optimize,
});
exe.root_module.addImport("hypergraphz", hypergraphz.module("hypergraphz"));

Example

examples/coauthorship.zig models a small research community as a directed hypergraph where vertices are researchers and hyperedges are papers. The first listed author is treated as the corresponding author, giving each hyperedge a natural direction.

zig build example

It walks through seventeen features of the library in sequence:

SectionAPIWhat it shows
Papers per researchergetVertexHyperedgesreverse-index query
Research communitiesgetConnectedComponentstwo isolated groups with no cross-group papers
Betweenness centralitycomputeCentralitywhich researchers bridge the most collaboration paths
Degrees of separationfindShortestPath1–3 hops within a community; unreachable across communities
Dual: paper-centric viewgetDualswap vertices ↔ hyperedges to see each researcher's papers
Shared authorshipgetIntersectionsresearchers present on every paper in a given set
Seniority orderingtopologicalSorthierarchy implied by the authorship direction
Pairwise expansionexpandToGraph6 papers encode 11 directed pairs in a regular graph
Spectral viewtoLaplaciannormalized Laplacian; block-diagonal under disjoint groups
PageRankcomputePageRankcitation-weighted stationary distribution; sums to 1
Random walkrandomWalkseeded weighted walk; stays inside the start's component
Star expansionexpandToStarbipartite (researcher ↔ paper) projection; 2-uniform
Line graphgetLineGraphpapers as vertices, linked when they share an author
K-core decompositiongetCore(s, t)-core peeling; trims the lone-paper researcher
NestednessgetInclusionsstrict-subset pairs across hyperedges; sized profile
Co-author neighborhoodsgetVertexNeighborhoodundirected co-occurrence neighbors across all hyperedges

Performance

Benchmarks run with zig build bench -Doptimize=ReleaseFast on an Intel Core i9-13900H, 64 GB RAM, CachyOS Linux (kernel 7.0.5).

Insertion — 1,000 hyperedges × 1,000 vertices

APITime
createVertex per vertex (atomic)77.7 ms
createVertex per vertex + appendVerticesToHyperedge (batch append)70.4 ms
createHyperedges + createVertices (bulk, single call each)47.6 ms
build() — construct reverse index over all 1,000,000 vertices227.8 ms

Queries (chain / shared-vertex graphs, after build())

OperationTime
`getVertexIndegree$ \times 100 — \text{each} \text{vertex} \text{in} 1{,}000 \text{hyperedges}422 µ\text{s}
$findShortestPath` — chain of 1,000 vertices232 µs
findCutVertices — chain of 1,000 vertices414 µs

Documentation

The latest online documentation can be found here.